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:
- Add a
.well-known/acme-challenge/location to your existing nginx server block. - Place the challenge file, request the cert from Let's Encrypt, and verify ownership over HTTP.
- Append
listen 443 ssl, thessl_certificateandssl_certificate_keydirectives, and an HTTP→HTTPS redirect to the server block. - 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:
| Domain | Path |
|---|---|
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
| Symptom | Likely cause |
|---|---|
ERR_CERT_COMMON_NAME_INVALID | Cert 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 domains | DNS 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 cert | nginx 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. |