Skip to main content
Version: Next

TLS / Certificates

The reference deployment terminates TLS at nginx and uses Let's Encrypt certificates issued by Certbot. Each public domain gets its own certificate; certs renew automatically via the certbot systemd timer.

Issuing a new certificate

For a fresh domain that already resolves to your host:

certbot --nginx -d platform.your-domain.com

Certbot will:

  1. Add a .well-known/acme-challenge/ location to your existing nginx server block.
  2. Place the challenge file, request the cert from Let's Encrypt, and verify ownership over HTTP.
  3. Append listen 443 ssl, the ssl_certificate and ssl_certificate_key directives, and an HTTP→HTTPS redirect to the server block.
  4. Reload nginx.

Repeat for each Honeyframe domain (Platform, Cloud, plus the App domain if a vertical app is enabled).

Cert paths

Certbot stores certs in /etc/letsencrypt/live/<domain>/. If certbot ever issues a renewal that requires a new directory (e.g. you re-issued for the same domain after deletion), it suffixes with -NNNN:

DomainPath
platform.your-domain.com/etc/letsencrypt/live/platform.your-domain.com/
cloud.your-domain.com/etc/letsencrypt/live/cloud.your-domain.com/
app.your-domain.com/etc/letsencrypt/live/app.your-domain.com/

Verify what certbot is currently managing:

certbot certificates

Renewal

Certbot installs a systemd timer (certbot.timer) that runs certbot renew twice daily. Renewals are no-ops until the certificate is within 30 days of expiry. Force a dry run to confirm:

certbot renew --dry-run

If a renewal succeeds but nginx is not reloaded, the new cert is on disk but the running nginx process still serves the old one. Certbot's deploy hook reloads nginx; if you've customized the install, ensure /etc/letsencrypt/renewal-hooks/deploy/ contains a script with systemctl reload nginx.

Manually wiring TLS for a server block

If you can't run certbot in --nginx mode (locked-down host, custom CA, internal-only deployment), edit the server block directly:

server {
listen 443 ssl http2;
server_name platform.your-domain.com;

ssl_certificate /etc/letsencrypt/live/platform.your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/platform.your-domain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

# ... rest of the server block ...
}

# Redirect plain HTTP to HTTPS.
server {
listen 80;
server_name platform.your-domain.com;
return 301 https://$host$request_uri;
}

Reload after editing: nginx -t && systemctl reload nginx.

Internal CA / private trust

If you operate an internal certificate authority (e.g. on an air-gapped install), point ssl_certificate and ssl_certificate_key at your CA-issued cert and skip certbot entirely. Honeyframe does not pin certificates and does not validate the issuer chain on the server side; clients (browsers, mobile apps) must trust your CA.

For mobile clients, ensure the CA root is in the device trust store — Android requires the CA to be installed under "User credentials" and the app to opt in via network_security_config.xml.

Renewing without disruption

The certbot renew flow re-uses the same key path, so neither nginx nor the backend services need a restart. If you rotate the key (re-issue with --force-renewal), reload nginx for the new key to take effect; the backend services are unaffected.

Common failure modes

SymptomLikely cause
ERR_CERT_COMMON_NAME_INVALIDCert was issued but the server block lacks listen 443 ssl + cert paths. Certbot's auto-edit is the usual fix; if you edited manually, verify both directives are present.
Certbot failed to authenticate some domainsDNS hasn't propagated, or port 80 is firewalled. Confirm the A/AAAA record resolves to your host and that nginx is listening on 80.
Renewal works but browsers still show old certnginx wasn't reloaded after renewal. Add systemctl reload nginx to /etc/letsencrypt/renewal-hooks/deploy/.
certbot renew says "renewal configuration file is broken"Usually a stray --no-autorenew in /etc/letsencrypt/renewal/<domain>.conf. Remove the line and re-run.