Custom domains, DNS & SSL
Add custom and wildcard domains, point DNS at your app, and set up HTTPS with a connected provider or manual TXT verification.
When you first deploy, Openship gives your app a working web address for free. This guide is about the next
step: putting it on a name you own, like app.example.com, with the little padlock (HTTPS) turned on
automatically.
Here's the idea in plain terms. A domain is the name people type to reach your app. DNS is the
internet's phone book — it's how a name like app.example.com gets connected to the actual computer running
your app. SSL (that padlock) is what makes the address start with https:// and keeps traffic private.
Openship handles the padlock for you; your only real job is adding a couple of entries to your domain's phone
book. This guide walks you through all of it.
What you need first
- A project already deployed in Openship (see Your first deployment).
- A domain you own, bought from a registrar (Namecheap, Cloudflare, GoDaddy, Google Domains, etc.).
- Access to that registrar's DNS settings — the page where you add records. That's where you'll paste the two lines Openship gives you.
For a self-hosted server behind Cloudflare Tunnel, use managed tunnel ingress to publish custom hostnames and optionally protect them with Cloudflare Access.
Choose the primary hostname during deployment
In the deployment wizard, Include www adds the companion hostname. For
Cloud and self-hosted destinations, the hostname you typed stays primary and the companion
redirects to it with HTTP 301. Under Domain behavior, choose either hostname
as primary, or Serve both to serve the app on both addresses.
The domain list shows which hostname redirects and where it sends visitors. Redirect-only hostnames do not need a separate app port or static path. Existing projects keep their current choice until you change it. Both hostnames still need DNS records, verification and HTTPS.
The same choices are available for Cloud deployments opened through Desktop.
Steps
Open your project's Domains tab
Go to your project and open the Domains tab. This is home base for everything about addresses and HTTPS.
Add your domain
Press Add domain. In the form that opens, choose Custom domain, then type the full address you want,
like app.example.com.
If your project runs a server, you'll also see a Maps to port field — this is which internal port the domain sends traffic to. Openship pre-fills it with your app's port, so you can usually leave it.
There are two optional toggles:
- Include www — also claim the
www.version of your domain. It becomes a domain in its own right: it gets its own DNS record, its own Verify, and its own certificate, and by default it sends visitors on to the bare domain with a301redirect (see Redirect one domain to another if you'd rather have it the other way round, or have both serve the app). - External ingress & TLS — turn this on only if your own edge (like a Cloudflare Tunnel or a load balancer) already handles HTTPS. Most people leave it off. See DNS setup, three ways below.
www needs its own DNS record
Because www.example.com is a separate domain, it needs a separate DNS record — Openship lists it alongside
the bare domain's in the next step. Skip it and www will stay Pending: it can't be verified, so it never gets
a certificate.
When the form is filled in, press the Add domain button at the bottom of the form to save it. (Yes, it's the same label as the button you just clicked — the first one opens the form, this one saves it.)
Adding a domain doesn't put it live yet
Saving the domain just registers your intent. It stays Pending until you add the DNS records and verify — that's the next two steps.
Copy the DNS records Openship shows you
Right after you add the domain, a DNS Records panel appears with the exact entries to create at your registrar. Each row has a Type, a Host, and a Value, and a Copy button next to it.
The panel shows the records for your deployment:
- A routing record (an
AorCNAME— Openship picks the right one for you, see below) that points the name at your app. - On Openship Cloud, an ownership TXT record at
_openship-challenge.<hostname>. Self-hosted HTTP verification does not need that record. Wildcard HTTPS uses a separate_acme-challengeTXT value generated by the certificate authority; see Wildcard domains.
Copy each value exactly as shown — don't retype it, use the Copy button.
If Cloudflare hosts your DNS, choose Connect a DNS provider in this panel. An owner or admin can add a token with Zone:Read and DNS:Edit for the relevant zones without leaving the deployment setup. The connection is saved in Settings → Credentials and can be reused across the workspace. Connecting verifies the token; it does not change DNS records.
Credentials belong to the instance where you connect them. For a Cloud project opened through a linked local or desktop instance, connect the provider in the Cloud dashboard; local credentials are not copied to Cloud.
For a saved domain, review the proposed additions or changes, then click Auto-configure DNS. Existing values are shown before repointing a record, unchanged records are skipped, and conflicting record sets are left for manual review. The record list refreshes after applying.
During a new deployment, you can connect the provider ahead of time. If the domain has not been saved yet, deploy first and apply the records from its DNS settings afterward, or use Manual DNS setup to publish them now. Cloudflare is currently the supported provider for automatic record writes; other providers use the manual steps below.
Paste them into your registrar
Open your domain registrar in another tab and find its DNS or DNS records page. Add each record from Openship — matching the Type, Host, and Value. Save.
What does “Host” mean at my registrar?
Registrars phrase it differently — you might see "Host", "Name", or "@". Paste the host exactly as Openship
shows it. If your registrar rejects @, it usually means "the root of the domain"; and a host like
_openship-challenge is added as a subdomain entry. When in doubt, copy Openship's value verbatim.
Verify
Back in Openship, click Verify next to the domain. On a self-hosted or Desktop-connected server, Openship obtains or reuses a certificate before reporting success. On Openship Cloud, it checks the routing and ownership records, then requests managed HTTPS. A wildcard on your own server opens DNS-verified HTTPS, where you choose a connected DNS provider or manual TXT verification.
DNS is not instant
After you save records at your registrar, they can take a few minutes — occasionally up to 48 hours — to spread across the internet. If Verify doesn't work on the first try, wait a bit and click it again. Openship also re-checks ordinary pending domains when the verification job is enabled. Manual TXT challenges wait for you to publish the record and press Check TXT & enable HTTPS.
Watch HTTPS turn on
The SSL status becomes Active after the certificate is installed. On your own server, verification
includes this step. On Openship Cloud, managed HTTPS can remain Provisioning after ownership is verified.
Once routing is configured and SSL is active, your app is reachable at https://app.example.com.
Certificates renew automatically with HTTP verification or a connected DNS provider. Manual TXT certificates
require a new TXT challenge at renewal; the domain details show when renewal is due.
Wildcard domains
Add *.example.com to the project's Domains tab or the service's public routes. Openship
automatically selects DNS-01 for HTTPS; HTTP verification cannot issue wildcard certificates.
The wildcard certificate covers app.example.com and api.example.com. It does not cover
example.com or deep.app.example.com. Add the base domain separately if it should serve the app too.
Choose a concrete hostname for the project's primary address and for links you open in a browser.
On a self-hosted or Desktop-connected server, deploy the project, then open DNS-verified HTTPS on the wildcard's card. The inline panel separates routing DNS from certificate verification:
- Point the domain at this server. Add the displayed routing record, usually
Awith name*and your deployment server's IP. You can also preview and apply it through a connected provider. This record sends application traffic to the server; it is separate from the certificate TXT. - Enable HTTPS. Choose one of the methods below. Progress and Certificate logs stay in the panel. Existing certificates remain installed if an attempt fails.
Automatic provider
Connect Cloudflare from the DNS panel or Settings → Credentials, using a token with Zone:Read and DNS:Edit for the relevant zone. Choose Automatic provider, then Enable HTTPS automatically. Openship publishes the certificate challenge, waits for DNS, obtains the certificate and removes its temporary TXT record. Keep the connection enabled so Openship can renew the certificate automatically.
The routing record still needs to point at the deployment server. A successful DNS certificate challenge proves domain control; it does not prove that application requests reach the correct port.
Manual TXT
Use this with any DNS provider when you do not want to connect an API credential:
- Select Manual TXT and press Get TXT record.
- Copy the exact Name and Value from the generated TXT card. For
*.example.com, the full name is_acme-challenge.example.com. In a provider that appendsexample.comautomatically, enter_acme-challengeas the name. For*.apps.example.com, enter_acme-challenge.apps. - Add that TXT value alongside any existing values at the same name. Do not replace unrelated verification records. Wait for DNS propagation, then press Check TXT & enable HTTPS.
- Once the panel says HTTPS is ready, remove only this attempt's TXT value if desired.
You can leave the page, reconnect, or restart Openship and return to the saved challenge. Retrying an active attempt reuses its order. Complete it before the deadline shown in the panel; an expired or invalid challenge needs a new attempt. Cancel challenge lets you discard the attempt or switch methods. Installation that has already started must finish.
Manual TXT also means manual renewal
When the certificate is within 14 days of expiry, the domain details show that renewal needs attention. Choose Renew SSL, publish the new TXT value and complete the check. Openship does not repeatedly attempt unattended issuance for a manual domain. To enable automatic renewal, connect Cloudflare, open the setup panel and choose Automatic provider.
Cloud projects and domains marked External ingress & TLS use their existing managed or external HTTPS flow. The local TXT panel applies to certificates Openship installs on your own deployment server.
SDK and MCP
The SDK and MCP use the same saved challenge and permission checks as the dashboard. With an initialized SDK client and the saved domain's ID:
await client.domains.startDnsChallenge(domainId, { mode: "manual" });
const pending = await client.domains.dnsChallenge(domainId);
// Read status until it is "waiting", then publish pending.record in DNS.
if (pending?.status === "waiting") {
await client.domains.checkDnsChallenge(domainId, { attemptId: pending.id });
}
// Read status again for completion, errors and logs.For renewal, start with { mode: "manual", force: true }. To cancel, call
client.domains.cancelDnsChallenge(domainId, { attemptId }). The
Domains API reference and MCP tools reference list the equivalent
start, status, check and cancel tools. Read status while work is running; stop polling while waiting
for someone to publish DNS or after the attempt settles.
Retry routing with logs
For a deployed project, open Domains and choose Retry routing from the warning banner, the page action, or a domain's menu. This repairs the project's saved routes and checks pending domains and HTTPS. A domain card whose backing record is missing also offers Retry routing directly.
The log viewer shows progress and the reason for any failure, including SSH connectivity and certificate errors. Working domains retain their state when another domain fails. After the attempt, the cards refresh from the server; the warning clears only when the checks succeed. Existing usable certificates are reused.
Closing the viewer stops watching; an accepted repair continues on the server. If the connection drops before completion, the viewer reports an unknown outcome and keeps the logs it received.
DNS setup, three ways
Openship figures out which records you need based on where your project runs. You don't choose the first two — they're picked for you. The records panel tells you which one applies.
Your project runs on your own server, so the domain points straight at that machine's public IP address.
| Type | Host | Value |
|---|---|---|
A | @ | your server's public IP |
The A record is guidance. Self-hosted verification obtains a certificate through ACME; it does not
compare public DNS with your server IP or require an ownership TXT record. The hostname must reach the
server for the selected ACME challenge to succeed.
Your project runs on Openship Cloud, so the domain points at a Cloud address using a CNAME (a "this name is
an alias for that name" record).
| Type | Host | Value |
|---|---|---|
CNAME | @ | an Openship Cloud target |
TXT | _openship-challenge | a long verification string |
Openship confirms the CNAME and the TXT, then handles HTTPS for you.
For advanced setups where your own edge already terminates HTTPS — a Cloudflare Tunnel, a load balancer, or another reverse proxy in front of Openship. Turn on External ingress & TLS when adding the domain.
| Type | Host | Value |
|---|---|---|
TXT | _openship-challenge | a long verification string |
On Openship Cloud, add the TXT record above to prove domain ownership. On a self-hosted instance, the operator already owns the server: no DNS record is requested or checked. Openship does not issue a certificate in either case; the SSL status reads External TLS.
For a homelab behind Nginx Proxy Manager, point your internal DNS name at NPM and configure it to forward to the Openship server reachable on your LAN or tunnel. NPM owns HTTPS. Choose External ingress & TLS for the domain, then verify it. Your rotating home WAN address is not a verification requirement. Clients outside your LAN still need a reachable address, VPN, or tunnel; private DNS alone does not provide public access.
Redirect one domain to another
Any domain on a project can send its visitors to another domain on the same project instead of serving the
app. Openship answers a 301 (or 302, if you pick it) and keeps the rest of the URL, so
https://www.example.com/pricing?ref=x lands on https://example.com/pricing?ref=x.
Open your project's Domains tab, press Edit, and set Redirect on the domain you want to send
away. Pick the destination from the list — it only offers this project's own domains — and choose whether
it's permanent (301) or temporary (302). Choose Serve the app here to turn a redirect off again.
The two common uses:
- Pick a canonical hostname.
www.example.com→example.com, or the reverse. This is what Include www sets up for you (pointingwwwat the bare domain), and you can flip it either way. Without a redirect both hostnames serve the same app, which search engines treat as duplicate content. - Move to a new domain. Keep the old one verified and redirect it to the new one, so existing links and bookmarks keep working.
A redirecting domain still needs DNS, verification and a certificate
It has to answer https:// before it can redirect anyone, so it goes through the same setup as any other
domain. That's also why it keeps showing its own Status and SSL rows.
Two things Openship won't let you do, because both break the site rather than the setting: redirecting to a
domain outside the project (that would turn a domain you own into an open redirect for someone else's traffic),
and creating a loop — A → B → A would bounce every request until the browser gave up.
Self-hosted deployments apply redirects through the server’s edge configuration. Openship Cloud applies the same redirect intent through its managed edge.
Bring your own certificate
For setups where your upstream needs the origin to present a real certificate too — most commonly Cloudflare Full (strict) — upload your own instead of using Let's Encrypt.
- On a verified domain, open its menu and choose Upload certificate.
- Paste the certificate (full chain, PEM) and the matching private key (PEM). A free Cloudflare Origin CA certificate (15-year) is a great fit here.
- Openship serves HTTPS from that certificate and stops running Let's Encrypt for this domain.
Uploaded certificates don't auto-renew
There's nothing for Openship to renew, so it watches the expiry and reminds you — upload a fresh certificate before it lapses.
CLI
The CLI supports ordinary domain creation and verification. The add command prints the DNS records and
the domain's ID; use that ID to verify. For manual TXT, use the dashboard, SDK, API or MCP lifecycle above.
# See exactly which records a domain will need (nothing is saved)
openship domain preview app.example.com
# Add the domain to a project (prints the records + the domain ID)
openship domain add app.example.com --project <projectId>
# Once your DNS is in place, verify it
openship domain verify <domainId>
# List a project's domains and their status
openship domain list --project <projectId>Other domain commands
openship domain records <domainId> reprints the records for an existing domain, openship domain primary <domainId> makes a domain the project's main address, and openship domain renew <domainId> /
openship domain verify-ssl <domainId> manage the certificate. Full details are in the
Domains API reference.
Troubleshooting
Verify keeps failing
Read the failure message for the selected verification mode. Cloud failures identify the routing or ownership
record. Self-hosted failures identify the certificate challenge or server connection. For manual TXT, compare
the exact _acme-challenge name and value, then retry the saved challenge from DNS-verified HTTPS.
DNS is not propagating
This is the most common hold-up and it's usually just time. DNS changes can take minutes, and occasionally up to 48 hours, to spread worldwide. Save the records, then wait and retry Verify — Openship also re-checks ordinary pending domains automatically when the job is enabled. Manual TXT waits for your check. Check for a typo in the record name or a conflicting routing record. Remove conflicting routing records as needed, but keep unrelated TXT values at the challenge name.
Verified, but SSL is stuck on “Provisioning”
The certificate is still being issued, or a request to Let's Encrypt hasn't completed. Give it a minute, then open the domain's ⋯ menu and click Recheck SSL — a quick, no-cost check that flips the status to Active once the certificate is actually in place. If it never provisions, use Renew SSL to try again; the error message will name the real reason.
The bare domain works but www doesn't (or the other way round)
They're two separate domains, so they're set up separately. Check the Domains tab: whichever one is still
Pending is missing its DNS record. Openship listed both records when you added the domain — open the failing
domain's DNS panel again, add the one it's asking for, then press its own Verify. A working apex tells you
nothing about www; each has its own record, verification and certificate.
Verify failed and you want the actual reason
On a self-hosted install, Verify streams the certificate request live and the log stays on screen whatever the
outcome — that log names the real cause (DNS not resolving, port 80 unreachable, a proxy answering 404). Use
Copy log if you want to keep it, and Try again to re-run without closing the window.
Related guides
Environment variables & secrets
Give your app its settings and secrets — add them in the Configuration tab or the CLI, keep the sensitive ones masked, and apply changes without a full rebuild.
Cloudflare Tunnel ingress
Publish custom domains through a server-owned Cloudflare Tunnel, with optional Cloudflare Access policies and ownership-checked cleanup.