Guides

HTTPS and certificates

How Synq gets free Let's Encrypt certificates over HTTP-01 without Cloudflare, issues wildcard certificates through a DNS provider, installs your own certificate, redirects HTTP to HTTPS, and renews everything.

Every Synq server runs an edge: OpenResty listening on ports 80 and 443 in front of all your apps. The edge ends HTTPS for each domain with its own certificate. This page explains where those certificates come from and what you can change.

In short:

  • A normal domain gets a free Let's Encrypt certificate over HTTP-01. You only need an A record and port 80 open. No Cloudflare account, no DNS provider, no API token.
  • A wildcard (*.example.com) needs DNS-01, which needs a connected DNS provider (Cloudflare today).
  • You can upload your own certificate instead, for example a Cloudflare Origin CA certificate.
  • Once a real certificate is in place, plain http:// is redirected to HTTPS automatically.
  • Certificates renew by themselves 14 days before they expire.

Let's Encrypt over HTTP-01 (no Cloudflare needed)

HTTP-01 is how Let's Encrypt checks that you control a name: it asks for a file at http://<hostname>/.well-known/acme-challenge/<token> on port 80. The edge answers that request itself, so your apps keep serving while it happens, and a successful answer proves the name points at this server.

What the domain needs

  1. An A record for the hostname pointing at the server's public IPv4 address. The Domains page and the project's DNS records panel show the exact value.
  2. Port 80 reachable from the internet. Open it in the server's firewall and in any cloud security group or NAT in front of it. Port 443 must be open for visitors too.
  3. No AAAA record pointing somewhere else. Let's Encrypt tries IPv6 first. Either point the AAAA at this server or delete it.
  4. No other A records for the same name pointing at other machines; the challenge may land on one of them.
  5. The Synq edge owns port 80. If nginx, Apache or Caddy still holds it, take it over first (see The edge).
  6. With a CDN in front (for example Cloudflare's proxy, the orange cloud), turn the proxy off (DNS only) until the certificate is issued, or make sure the CDN forwards plain HTTP on port 80 to the server.

Verification is the certificate

On a Synq server, "verifying" a domain means obtaining its certificate. Synq doesn't compare your DNS with the server's IP and doesn't ask for an ownership TXT record: a certificate can only be issued if the name really reaches this server, which is the proof. The live DNS check on the Domains page is advice to help you get there.

Issue a certificate

You can start issuance from three places. All of them stream certbot's log live, so you see the real reason if it fails.

  • Domains page: open the row's ⋯ menu and choose Verify and issue certificate (or Issue certificate for a domain that's already verified).
  • Project → Domains & Routes: press Verify {hostname} under the DNS records, or the Verify button on the domain's card.
  • CLI: synq domain verify <domainId> (get the ID from synq domain list --project <projectId>).
synq domain list --project <projectId>
synq domain verify <domainId>

A new domain whose project has never deployed doesn't request a certificate yet: the first deploy creates the route, and the certificate follows once the domain verifies. You don't need to redeploy after a successful verify; the edge re-registers the existing route with TLS.

If the server already holds a valid certificate for the name with more than 14 days left, Synq reuses it and doesn't ask Let's Encrypt again ("a valid certificate is already present; no new certificate was requested").

Automatic retries

You don't have to keep pressing Verify. The Domain DNS verification job (domains:verify-pending) runs about every 13 minutes. It first checks a pending domain once it is 10 minutes old, then backs off after each failure: 15 minutes, 30 minutes, 1 hour, 2 hours, 4 hours, then every 6 hours. It also finishes certificates for verified domains whose first issuance failed. Only domains of projects with a live deployment are retried. The domain's details show the Next scheduled check; press Verify for an immediate retry. You can see and change the job under Jobs in the sidebar.

Mind Let's Encrypt's rate limits

Let's Encrypt limits how often a name may fail validation and how many certificates you can get for the same names each week. When issuance fails, read the reason in the log and fix it before retrying. To experiment freely, switch to the staging directory (see Certificate authority settings).

Wildcard certificates with a DNS provider

A wildcard certificate such as *.example.com covers every single-level subdomain (a.example.com, b.example.com) with one certificate. Let's Encrypt only issues wildcards through DNS-01: Synq creates a temporary _acme-challenge TXT record through your DNS provider's API, waits for Let's Encrypt to read it, then deletes it.

Connect your DNS provider

Cloudflare is the supported provider. In Cloudflare, create an API token at dash.cloudflare.com/profile/api-tokens with Zone:Read and DNS:Edit on the zones Synq should manage.

In Synq, open Settings → Credentials (organization admins), choose Cloudflare, give it a Label, paste the API token and save. Synq checks the token with Cloudflare before storing it, encrypted. The Certificates tab of the Domains page then says "A DNS provider is connected, so wildcard certificates are available."

Point the wildcard at the server

At Cloudflare, add an A record for * (that is, *.example.com) pointing at the server's IP, so traffic for the subdomains reaches the edge.

Add the wildcard domain

Open the project's Domains & Routes tab, press Add domain, choose Custom domain and type *.example.com. Use DNS-01 challenge switches on by itself and can't be turned off for a wildcard ("Wildcard certificates require DNS-01 and a connected DNS provider."), and Include www is disabled. Press Add domain. The Domains page wizard accepts a wildcard hostname too.

Verify

Press Verify. The log shows "Requesting a certificate for *.example.com (DNS-01 via connected DNS provider)" and, when it succeeds, the certificate is installed for every subdomain.

You can also use DNS-01 for a normal hostname: turn on Use DNS-01 challenge when adding it ("Issue and renew the certificate through your connected DNS provider. Use this when port 80 cannot be reached."). Renewals then use DNS-01 too.

Limits:

  • A wildcard covers one level: *.example.com covers app.example.com but not example.com itself or a.b.example.com. Add the apex as its own domain if you need it.
  • Only Cloudflare is supported as a DNS provider. Without a connected provider, verification stops with "DNS-01 challenge for *.example.com requires a connected DNS provider (e.g. Cloudflare) in Settings → DNS." (the provider lives under Settings → Credentials in the current panel).
  • If Cloudflare rejects the stored token, the message starts with "DNS provider credential rejected". Replace the token under Settings → Credentials.

Free subdomains don't need a wildcard certificate

The free <app>.<base> subdomains rely on a wildcard DNS record (*.<base>), not a wildcard certificate. Each app gets its own HTTP-01 certificate, so no DNS provider is needed for them.

Upload your own certificate

Use this when the certificate must come from somewhere else:

  • Cloudflare Full (strict): Cloudflare connects to your server over HTTPS and requires a certificate it trusts. A free Cloudflare Origin CA certificate (valid up to 15 years) is a good fit.
  • A certificate from your company's CA, or one you already bought.

Verify the domain first

The domain must be verified before a certificate can be installed. A domain behind a proxy that owns TLS is usually added with External ingress & TLS, which verifies without issuing anything.

Open the upload dialog

On the Domains page, open the row's ⋯ menu and choose Upload certificate, or use Upload certificate in the domain's menu on the project's Domains & Routes tab.

Paste the PEM files

Paste the Certificate (PEM, full chain), starting with -----BEGIN CERTIFICATE----- and including the intermediates, and the matching Private key (PEM). Press Install certificate.

Before touching the edge, Synq checks that the certificate parses, that the key matches it, that it covers the hostname and that it hasn't expired. A certificate that fails is refused with "Invalid certificate: ..." and nothing changes, because a broken pair would stop OpenResty from reloading for every other domain too.

After the upload the certificate shows as Uploaded, with Auto-renew: Manual. Synq stops running certbot for that domain and never renews it. There is no expiry reminder for uploaded certificates: watch the countdown on the Certificates tab and upload a new certificate before it lapses.

HTTPS redirect

You don't switch the redirect on; it follows the certificate. The HTTP to HTTPS column on the Domains page shows the current state:

StateBehaviour
OnThe domain has a real certificate (issued or uploaded). Every plain http:// request gets a 301 to https://<hostname><path>?<query>, on every path the domain serves.
After certificateNo certificate yet. HTTP requests are served normally so the site works while the certificate is pending; the redirect starts as soon as it is active.
UpstreamTLS is handled by your own proxy or CDN (external ingress). Visitors reach HTTPS through that proxy, so configure the HTTP to HTTPS redirect there.

Two details matter behind a CDN:

  • A request that arrives with the header X-Forwarded-Proto: https is not redirected. That header means a CDN (for example Cloudflare in Flexible mode) already served HTTPS to the visitor and reached the server on port 80; redirecting it would loop forever. The app is told the request was HTTPS.
  • Let's Encrypt's challenge path /.well-known/acme-challenge/ is never redirected, so renewals keep working.

Synq doesn't send an HSTS header. If you want one, add it for a domain with custom proxy config.

Renewal

The SSL certificate renewal job (ssl:renew) runs once a day at 03:17 (server time). It renews every certificate that Synq issued and that has fewer than 14 days left, up to 50 per run. Uploaded certificates and domains whose TLS ends at your own proxy are skipped.

  • The Automatic renewal card on the Domains page's Certificates tab shows the next and last run.
  • Renew {n} due on the Domains tab renews every due certificate right away.
  • Renew certificate in a row's menu forces one renewal; synq domain renew <domainId> does the same, and synq domain renew-all renews every near-expiry certificate in your organization.
  • Recheck certificate (synq domain verify-ssl <domainId>) only reads what's on the server, without asking Let's Encrypt, and updates the state.

When the job fails to renew a certificate, the domain's certificate turns Failed and an SSL cert expiring notification is sent (see Notifications).

synq domain renew <domainId>
synq domain renew-all
synq domain verify-ssl <domainId>

Certificate authority settings

By default certbot uses Let's Encrypt's production directory. You can point it at another ACME certificate authority, add a contact email, or change the key type with environment variables on the server. The Certificate authority card on the Certificates tab shows the values in effect.

VariableWhat it sets
SYNQ_ACME_EMAILContact email for the ACME account; the CA sends expiry notices here. With no email at all, certificates are requested without a contact address. An instance administrator can override it on the Certificates tab.
SYNQ_ACME_DIRECTORY_URLACME directory. Unset means Let's Encrypt production. Staging is https://acme-staging-v02.api.letsencrypt.org/directory; ZeroSSL is https://acme.zerossl.com/v2/DV90.
SYNQ_ACME_EAB_KID and SYNQ_ACME_EAB_HMAC_KEYExternal account binding, required by CAs such as ZeroSSL. Set both or neither.
SYNQ_ACME_KEY_TYPEec256, ec384, rsa2048 or rsa4096. Unset uses certbot's default.
SYNQ_ACME_CA_BUNDLEAbsolute path to a CA root bundle, for a private ACME server whose certificate isn't publicly trusted.
SYNQ_ACME_TOS_AGREEDAccept the CA's terms of service non-interactively. Defaults to true.

To change them on a server installed with the installer, add the lines to /root/.synq/compose/.env on the server (your additions are kept across updates), then apply them with sudo synq up. For example, to test with the staging directory:

# on the server
echo 'SYNQ_ACME_DIRECTORY_URL=https://acme-staging-v02.api.letsencrypt.org/directory' | sudo tee -a /root/.synq/compose/.env
sudo synq up

The Provider row on the card then reads Let's Encrypt (staging). Browsers don't trust staging certificates, so remove the line and apply again to go back to production.

What visitors see before an app is live

The edge answers every hostname that reaches it, even when nothing is deployed there. So you know what you're looking at:

SituationWhat the visitor gets
Not deployed yet: a verified custom domain whose project has never had a live deploymentA 503 page titled "{hostname} is not deployed yet": "This domain is connected and ready. The site will appear here as soon as its first deployment goes live."
Last deploy failed before anything went liveA 503 page titled "{hostname} is not live": "The last deploy failed, so there is nothing to serve yet. See the dashboard for the deploy log."
The app's container isn't answering (restarting, crashed, booting)A 502 page titled "Application unavailable".
A hostname no route knows (a typo, a stray wildcard record, a visit by IP)A 404 page titled "Service not found".

The "not deployed yet" page is served with the domain's real certificate, so the browser shows a padlock rather than a certificate warning, and it tells browsers and crawlers to retry later (Retry-After: 120, Cache-Control: no-store, noindex). It appears after the domain is verified, and the first successful deploy replaces it with your app.

On this page