Domains, DNS & SSL
Fix domain verification, DNS propagation, missing routes, SSL provisioning, and external ingress.
A working custom domain needs DNS to reach its ingress, a route to the intended application, and TLS at the server or an external proxy. Verification depends on where the project runs. This page explains the failure messages and which part to check.
If you haven't added a domain yet, start with the custom domains guide — this page is for when something is already stuck.
Check the verification mode
Openship Cloud checks a routing record and an ownership TXT record; with external ingress it checks only the TXT record. Self-hosted verification obtains or reuses a valid certificate through ACME, without comparing the public IP or checking an ownership TXT record. Self-hosted external ingress accepts the operator's configuration and leaves TLS to the proxy.
DNS and SSL are verified, but the app still returns 404 or 502
DNS verification and certificate issuance do not prove that a domain has a working upstream. For a Compose project, open Domains & Routes and map the domain to the intended service's container port. An unmapped project-level domain is skipped with a warning naming the available ports; it must not inherit a stale project port from another service. Then retry routing or set up the project's edge. Review any route warnings even when the edge itself is healthy.
On an Openship control-plane project, an instance administrator can use these repair actions to route the adopted dashboard to its configured loopback port. The repair preserves the dashboard service's existing port claim. Project membership alone does not grant access to reserved host ports, and organization-bound or restricted API tokens cannot perform this instance action.
If setup recorded OPENSHIP_PUBLIC_URL as external ingress, boot assumes an existing proxy
terminates TLS. That record alone does not provision Openship's edge. Use the self-hosted setup
flow to configure Openship's edge when changing who owns ingress; retain external ingress when
your existing proxy should continue serving the domain.
A static route needs the absolute release directory of its active deployment. A container ID, image tag or artifact hash cannot serve as a document root. A warning that no static root exists means the deployment's artifact or workload metadata needs repair or a new successful deployment.
Verification keeps failing
Symptom / what you see
You click Verify and it doesn't turn green. The "Verification failed" toast contains the reason Openship returned. On Openship Cloud, DNS failures name one or both records:
CNAME record not found for app.example.comTXT record _openship-challenge.app.example.com must equal "…"
(If Openship can't produce a specific reason it falls back to the generic "{hostname} could not be verified yet.")
From the API, POST /api/domains/:id/verify returns 422 with the failure message. For Cloud DNS checks,
cnameVerified and txtVerified identify the failing record. On self-hosted instances, read the message
for certificate or server connectivity failures; those flags do not represent separate DNS lookups.
What it means. Cloud DNS verification could not confirm the requested routing or ownership record. Fix each record named in the message.
| Message you see | Which record | What's wrong |
|---|---|---|
CNAME record not found for … | Routing (CNAME) — Openship Cloud | The CNAME is missing or doesn't point at the Cloud target. |
TXT record _openship-challenge.… must equal "…" | Ownership (TXT) | The _openship-challenge TXT record is missing, or its value doesn't match the secret code. |
If the Cloud routing record is failing
How to fix
- Open your project's Domains tab and reopen the DNS Records panel for the domain (or run
openship domain records <domainId>). - Compare the routing row against your registrar's record. Cloud expects a
CNAMEpointing at the Openship Cloud target shown in the panel. - Fix any mismatch — copy the value from Openship, don't retype it. Delete any old, conflicting record for the
same name first (a stale
A/CNAMEleft in place is a common cause). - Wait for the change to take effect, then click Verify again.
Self-hosted: check the certificate challenge
For HTTP-01, the hostname must reach the server's challenge endpoint on port 80, including through any proxy or NAT. DNS-01 uses a connected DNS provider or the Manual TXT flow in DNS-verified HTTPS. Neither mode compares the hostname's public IP with the server's IP. If another proxy owns TLS, use External ingress & TLS and configure that proxy to forward application requests.
Wildcard or manual TXT setup is waiting
Wildcard certificates require DNS-01. The routing A or CNAME record does not replace the certificate's
_acme-challenge TXT record. Open the domain's DNS-verified HTTPS panel to see the active attempt,
its deadline and Certificate logs.
| State or message | What to do |
|---|---|
| No DNS provider connected | Choose Connect a DNS provider in the DNS panel (also available in Settings → Credentials), or choose Manual TXT and generate a TXT record. |
| TXT record not visible | Compare both name and value. If the provider appends your zone name, enter only the relative part. Keep other TXT values, allow propagation, then Check TXT & enable HTTPS. |
| Connection interrupted | Reopen the panel or use Refresh status. An accepted operation continues on the server; the saved attempt can resume after a restart. |
| TXT challenge expired or CA order invalid | Cancel the old attempt if needed and start again. Publish the new value; remove only the previous attempt's value. |
| Certificate authority changed | Start a new challenge using the current HTTPS settings. An order belongs to the authority that created it. |
| Server connection or certificate installation failed | Restore access to the current deployment server, then retry the same saved challenge before its deadline. Existing TLS is retained. |
Manual TXT setup does not retry on a schedule: it waits for the operator to publish and check DNS. Within 14 days of expiry, the domain details request renewal. Use Renew SSL to obtain and confirm a fresh TXT value, or switch to Automatic provider for unattended renewal. See the wildcard setup guide.
If the Cloud TXT ownership record is failing
How to fix
- At your registrar, add (or correct) a TXT record with host
_openship-challenge— Openship looks it up at_openship-challenge.<your-domain>. - Set its value to the exact string from the DNS Records panel. Use the Copy button; a single extra space or a missing character will fail the match.
- Save, wait for propagation, and click Verify again.
Cloudflare represents TXT content with surrounding double quotes, for example
"ownership-token". Automatic DNS setup writes this form and repairs older
unquoted values when you apply the DNS plan again. The quotes delimit the DNS
string; they are not extra characters in the ownership token. Openship accepts
quoted resolver answers and joins chunks within a single TXT record.
Cloud DNS is correct, but the certificate check fails
Openship Cloud gets its certificate status from the Cloud provider. Connecting Cloudflare automates DNS records; it does not move Cloud certificate issuance to the Openship API server. A Cloudflare certificate notification alone does not confirm the origin's certificate or the application's route.
If the response contains CLOUD_DOMAIN_NOT_CONNECTED, the provider has no route
for this domain and project. Check the service mapping in Domains & Routes,
retry routing, then verify HTTPS. CLOUD_DOMAIN_CHANGED means the binding changed
during the operation; refresh the domain details before retrying. Openship does
not adopt another project's route to repair this condition.
Older Cloud adapters could throw undefined is not an object while reading
info?.ssl.status, even after the provider issued a certificate. Update the
Openship API to a version that accepts both flat and nested provider SSL status
responses, then use Recheck SSL. Rechecking reads the existing certificate
and does not request another one.
Already verified?
If you re-run Verify on a domain that's already done, Openship simply replies "Already verified" and changes nothing — that's expected, not an error.
DNS hasn't propagated yet
Symptom / what you see
The records look correct at your registrar, but Verify still reports the routing or TXT record as not found — sometimes minutes after you saved them.
What it means. DNS isn't instant. After you save a record, it has to spread across the internet's caches before Openship's lookup can see it. As the DNS Records panel itself notes: "DNS changes can take up to 48 hours to propagate globally." Usually it's a few minutes; occasionally it's much longer.
How to fix
- Double-check there are no typos in the Host field and no leftover conflicting record for the same name.
- Wait. Save the records, give it several minutes, then click Verify again — re-clicking once DNS resolves is the reliable path.
- Openship's enabled-by-default Domain DNS verification job runs every 13 minutes. Once a pending domain is at least 10 minutes old, the job retries verification and certificate issuance automatically. This also runs in Openship Desktop against the project's connected deployment server. Clicking Verify is the faster, explicit retry; it is not required for the normal background lifecycle. Manual TXT domains are excluded from this job and wait for Check TXT & enable HTTPS instead.
What the first deployment does
A new custom domain starts Pending. Its deployment prepares the route and SSL toolchain. A DNS-01 certificate needs a connected provider, or manual TXT setup after deployment. A missing credential or certificate failure remains actionable in the domain details; it does not erase the deployed application.
Confirm propagation yourself
To check what the world currently sees, look the record up directly — e.g. dig TXT _openship-challenge.app.example.com
or dig A app.example.com. If your own lookup doesn't return the new value yet, Openship won't see it either;
that's a propagation delay, not an Openship problem.
SSL stuck on "Provisioning" (or no certificate issued)
Symptom / what you see
The domain is Verified, but the SSL pill stays on Provisioning instead of moving to Active —
so https:// doesn't work yet.
What it means. On a self-hosted or Desktop-connected server, Verify obtains the certificate before reporting success and rewrites the existing route with TLS. On Openship Cloud, verification can start certificate issuance in the background. Provisioning means issuance is still in progress, its result has not been observed yet, or the first attempt failed.
How to fix
- Give it a moment, then open the domain's ⋯ menu and click Recheck SSL. This is a read-only check (no certificate is re-issued, no rate limit is spent) that flips the status to Active as soon as the certificate is actually in place.
- If Recheck reports "No valid certificate found for {hostname} yet. If you just deployed, give Let's Encrypt a moment, then recheck." — the certificate genuinely isn't there yet. Wait a little longer and recheck again.
- The same scheduled domain job retries incomplete issuance with backoff. If you want an immediate retry, click Renew SSL; any real failure reason (DNS, blocked ports, rate limit, ACME outage) surfaces on that action.
Renewing certificates
Once a certificate is Active you normally never touch it again. Renewal targets certificates within about two weeks of expiry: on Openship Cloud, and on self-hosted setups where your proxy or edge (Traefik, Caddy, a load balancer) owns TLS, this is handled for you; a self-hosted operator can also point a scheduled job at the renew endpoint. Renew SSL is the manual fallback if a certificate ever lapses. If you chose manual TXT, this opens DNS setup for a fresh record; those certificates cannot renew unattended.
“Recheck / Renew SSL” errors that the domain must be verified
SSL actions require a verified domain — attempting them on a pending one returns "Domain must be verified before SSL can be managed". Finish verification first (above), then manage the certificate.
Verify should not require a redeploy
The first deployment prepares certbot even while the domain is pending. After a successful Verify, the certificate provider re-registers the existing route with TLS, so no redeploy is expected. If a current deployment only starts serving HTTPS after redeploying, preserve both the deployment log and the Verify log; that indicates a separate edge/route reconciliation failure.
External ingress: your edge terminates TLS
Symptom / what you see
Your app already sits behind a Cloudflare Tunnel, a load balancer, or another reverse proxy that handles
HTTPS — but Openship keeps asking for an A/CNAME and tries to issue its own certificate, which you don't
want.
What it means. Standard verification lets Openship manage the certificate. With an external proxy, that proxy owns public TLS. External ingress keeps certificate issuance out of domain verification; Cloud still checks ownership, while self-hosted installations accept the operator's configuration.
How to fix
- When adding the domain, turn on External ingress & TLS. From the API, pass
"externalIngress": trueonPOST /api/domains. - On Openship Cloud, add the requested
_openship-challengeTXT record. On self-hosted instances, no DNS record is requested or checked. Configure DNS to reach your proxy, including internal DNS for a homelab behind Nginx Proxy Manager; a rotating WAN IP does not block this verification mode. - Click Verify. Openship confirms "Domain verified — TLS is handled by your external ingress; no certificate is issued here." Your proxy must forward requests to the Openship server and serve HTTPS.
What the SSL pill shows
An external-ingress domain reads External TLS for its SSL status. Verification does not request a certificate from Openship; maintain the public certificate at your proxy. Any separately configured origin certificate protects the proxy-to-server connection.
Turn it on only if you mean it
If your edge does not handle HTTPS, leave External ingress off — otherwise the domain will verify but serve plain HTTP, with no certificate from anyone. When in doubt, leave it off and let Openship issue the certificate.
Still stuck?
One more thing to check: the hostname itself
If Add domain is rejected before you even reach verification, the hostname failed a basic check — e.g.
Domain "app.example.com" is already in use (it's attached elsewhere) or
"…" is not a valid public hostname. (a bare IP, localhost, a single-label name, or a dot-separated label
longer than 63 characters). Free *.opsh.io-style subdomains aren't added here either — those live in the
project's public endpoints, not as custom domains.
Custom domains guide
The full add → point DNS → verify → HTTPS walkthrough, click by click.
Domains API reference
Every domain endpoint, the preview/verify responses, and the cnameVerified / txtVerified flags.
Project Domains tab
Where domains, verification status, and the SSL pill live in the dashboard.
Deployment & build failures
The build went red — here's how to read the failure and fix the usual causes, from a wrong build command to a compose deploy that needs a decision.
GitHub connection & repos
Fix an expired or unauthorized GitHub connection, a private repo that won't import, a public repo that should just work, and auto-deploy webhooks that never fire.