Pointing a domain at a container with automatic HTTPS
Route your domain to a VPS running containers and configure automatic HTTPS using ACME, Certbot or a reverse proxy. This guide covers DNS setup, firewall rules, certificate issuance, common failure points and renewal monitoring.
Pointing a domain at a container with automatic HTTPS
You point your domain to the VPS that hosts the container, make sure ports 80 and 443 reach it, then let an ACME client or your reverse proxy obtain and renew a certificate. Automatic issuance fails if port 80 is closed or your DNS changes have not reached the authoritative nameservers. Once issued, monitor renewals so you do not wake up to an expired certificate.
This picks up from a server you can already reach over SSH.
Before you start
- You are deploying to a Linux VPS. With Hostworld you manage power, rebuilds and the VNC console in Virtualizor from our client area. The work below happens on the VPS itself over SSH.
- You control the domain’s DNS. You will create A and AAAA records for the host that runs your container. If your domain uses Hostworld DNS on our web hosting, your nameservers are ns1.serverworld.uk, ns2.serverworld.uk, ns3.serverworld.uk and ns4.serverworld.uk, and you edit DNS in cPanel. If your DNS is elsewhere, do this at your registrar or DNS provider.
- Decide your certificate path:
- Use a reverse proxy that does automatic HTTPS for you, such as Caddy or Traefik. This is the least work as long as ports 80 and 443 are reachable and you persist their storage.
- Use Certbot to obtain and renew certificates, then point your web server or proxy at them. On Ubuntu 24.04 the recommended install is via snap. On AlmaLinux 9 you install snapd from EPEL then use snap to install Certbot. Follow the official Certbot instructions linked below.
- Plan for common pitfalls:
- Let’s Encrypt’s HTTP-01 validation is fetched over port 80 at
http://yourdomain/.well-known/acme-challenge/<token>. It fails if port 80 is blocked by a firewall, your ISP or a redirect that never reaches a listener. - Let’s Encrypt prefers IPv6 if you publish both A and AAAA. A broken AAAA record, or nothing listening on 80 or 443 on IPv6, will make issuance or renewal fail even if IPv4 works.
- It takes time for changes to reach all authoritative nameservers. Let’s Encrypt validates from multiple network locations. If your records are not in sync across all authoritative servers yet, issuance can fail intermittently.
- Wildcard certificates require DNS-01. HTTP-01 cannot issue wildcards.
- CAA records can block issuance if they do not authorise Let’s Encrypt. If you publish CAA, include
issue "letsencrypt.org"and, for wildcards,issuewildtoo. - Certificate lifetimes are shrinking. Let’s Encrypt is moving from 90 days to 64 days, then 45 days. Automation and monitoring matter.
- Docker adjusts iptables on the host. Do not assume UFW alone controls published ports. Use the DOCKER-USER chain to enforce policy.
- Let’s Encrypt’s HTTP-01 validation is fetched over port 80 at
Step 1: Point your domain at the container host
Create DNS records so your domain resolves to the VPS that runs your containers.
- Create an A record at the zone apex (the bare domain) pointing to your VPS’s IPv4 address.
- If you have IPv6 on the VPS and will listen on IPv6, create an AAAA record at the apex pointing to the server’s IPv6 address. If you do not plan to listen on IPv6 yet, do not add AAAA until you are ready.
- Make
wwwa CNAME to the apex, or give it its own A/AAAA if you prefer. Do not put a CNAME at the apex. The apex already has SOA and NS data and a CNAME cannot coexist with other data.
Example records:
; At your DNS host
example.com. 300 IN A 203.0.113.10
example.com. 300 IN AAAA 2001:db8:1234::10
www.example.com. 300 IN CNAME example.com.
If your domain is using Hostworld DNS, the nameservers to set at your registrar are: ns1.serverworld.uk, ns2.serverworld.uk, ns3.serverworld.uk, ns4.serverworld.uk.
Step 2: Check DNS at the authoritative nameservers
You will query the authoritative nameservers directly so you know the source Let’s Encrypt will ask is in sync. Comparing with public resolvers like Google and Cloudflare helps you spot caching and propagation lag.
This shows your domain’s authoritative nameservers from a public resolver, so you can list them:
# Show the NS set as seen by Google Public DNS
dig +short NS example.com @8.8.8.8
# Show the NS set as seen by Cloudflare DNS
dig +short NS example.com @1.1.1.1
This asks each authoritative nameserver for the records you added. Replace nsX.host with each nameserver from the previous output.
# Query the apex A record at a specific authoritative server
dig +noall +answer example.com A @nsX.host
# Query the apex AAAA record (if you set IPv6) at the same server
dig +noall +answer example.com AAAA @nsX.host
# Query the CNAME for www at the same server
dig +noall +answer www.example.com CNAME @nsX.host
Repeat those queries against every authoritative nameserver. All should return the same answers. If they do not, wait for propagation before you request a certificate. Let’s Encrypt validates from multiple locations and can hit different authoritative servers. Unsynchronised DNS causes intermittent failures.
Step 3: Open ports 80 and 443 on the VPS firewall
Let’s Encrypt recommends keeping port 80 open. Automatic HTTPS tools also need 443. Open both on your server and make sure any external firewall or port forward you manage passes them through.
Ubuntu 24.04 (UFW)
This allows inbound TCP on ports 80 and 443 through UFW, the uncomplicated firewall:
sudo ufw allow proto tcp from any to any port 80,443
If UFW was disabled and you enable it now, be sure you have SSH allowed as well so you do not lock yourself out.
AlmaLinux 9 (firewalld)
This opens the predefined HTTP and HTTPS services permanently and reloads the firewall daemon to apply the change:
sudo firewall-cmd --add-service=http --add-service=https --permanent
sudo firewall-cmd --reload
Docker changes iptables directly. If you publish container ports, verify that your DOCKER-USER chain or equivalent nftables rules reflect the policy you intend. Do not assume UFW alone gates those ports.
Step 4: Choose and prepare your certificate method
Option A: Use a reverse proxy with automatic HTTPS
- Caddy obtains and renews certificates automatically if ports 80 and 443 are reachable from the Internet. It listens on 80 for HTTP-01 and to serve HTTP to HTTPS redirects. Keep its storage persistent so renewals survive restarts.
- Traefik supports Let’s Encrypt via HTTP-01 on 80, TLS-ALPN-01 on 443, or DNS-01. Persist the
acme.jsonfile and set its permissions to 600. Losing it forces re-issuance and risks rate limits. - Nginx Proxy Manager and similar tools obtain certificates via HTTP-01. They need both 80 and 443 forwarded to the proxy host. Blocking 80, or forcing an HTTPS redirect while 443 is not reachable, can make renewals fail.
For all of these, publish your container service behind the proxy, not directly. The proxy will terminate TLS and route to your app.
Option B: Use Certbot
- On Ubuntu 24.04, install Certbot via snap, and remove any older package-manager Certbot first so the snap version is used. See the official instructions at certbot.eff.org or Snapcraft.
- On AlmaLinux 9, install
snapdfrom EPEL, then install Certbot via snap. See the snap installation guide for AlmaLinux at Snapcraft. - Use HTTP-01 if port 80 is open and the challenge path can be served by your stack. Certbot’s HTTP-01 challenge is fetched at
/.well-known/acme-challenge/<token>. - If port 80 is blocked by your ISP or a policy you cannot control, use DNS-01 with a DNS plugin. Certbot does not support TLS-ALPN-01 yet. Do not try to work around this by blocking port 80. Let’s Encrypt recommends keeping 80 open.
- If you run Certbot inside a container, mount
/etc/letsencryptand/var/lib/letsencryptas persistent volumes. Without that, you will lose keys and hit rate limits by re-issuing on every restart.
Step 5: Verify HTTP-01 will be reachable
Let’s Encrypt fetches the exact path http://yourdomain/.well-known/acme-challenge/<token> during issuance and renewal. If you use a CDN or WAF, make sure it forwards that request unmodified over port 80. If it interferes, prefer DNS-01 using a scoped API token for your DNS provider. If you enforce HTTP to HTTPS redirects, keep port 80 open and ensure your proxy responds there so the redirect can be served.
If both A and AAAA exist, the validation will prefer IPv6. Make sure something is listening on ports 80 and 443 on your server’s IPv6 address or remove a bad AAAA record until IPv6 is ready.
Step 6: Issue the first certificate and confirm it is in use
If you use Caddy, Traefik or Nginx Proxy Manager, bring up the proxy with your domain configured. They will attempt issuance automatically once the site is reachable on 80 and 443.
If you use Certbot with a web server or standalone, follow the Certbot documentation for your web server to request the certificate. Do not delete anything in /etc/letsencrypt by hand. If you later want to remove a certificate lineage, use certbot delete. Avoid repeated re-issue attempts or --force-renewal during troubleshooting. You can hit rate limits quickly.
To confirm the live certificate from outside the server, this connects to your site with OpenSSL and prints the expiry date. Replace example.com with your domain.
# This connects to your site and shows the served certificate's end date
openssl s_client -servername example.com -connect example.com:443 </dev/null 2>/dev/null | openssl x509 -noout -enddate
Step 7: Make sure renewals are happening
Renewal must be automatic. With Caddy and Traefik, monitor their logs and confirm their storage persists across restarts. With Certbot installed via snap, a systemd timer handles renewals. Test before you rely on it.
This performs a simulated renewal. It does not change your live certificates. Use it for a health check after configuration changes.
# Dry-run all configured renewals
sudo certbot renew --dry-run
This lists upcoming systemd timers so you can confirm the Certbot timer is present, then shows the recent renewal logs. The unit names can vary slightly. Adjust if needed when you read the output.
# List timers, look for a certbot-related timer
systemctl list-timers
# Show logs for the certbot renew service
journalctl -u snap.certbot.renew.service --since "2 days ago"
If the dry-run fails, read the error carefully. Common causes are: HTTP-01 not reachable on port 80, AAAA pointing to an address where nothing listens, a CDN or WAF intercepting /.well-known/acme-challenge/, or a restrictive CAA policy that does not authorise Let’s Encrypt.
As certificate lifetimes shorten from 90 to 64 and eventually 45 days, do not push renewals right up to expiry. Some ACME clients support ACME Renewal Information (ARI) and will choose renewal windows and report successful replacement. Using a client that implements ARI reduces renewal spikes and makes it easier to react if a certificate needs revoking.
Step 8: Troubleshoot common failures
- DNS not propagated to all authoritative servers: Query each authoritative nameserver directly with
digusing the@serverform. Wait until they all agree before requesting a certificate. Let’s Encrypt validates from multiple locations and expects consistent answers. - Port 80 closed: Open port 80. Let’s Encrypt explicitly recommends keeping 80 open for HTTP-01 and redirects. If you cannot, switch to DNS-01 with a DNS plugin in your client. Certbot does not support TLS-ALPN-01 yet.
- IPv6 present but not working: Either enable listeners on ::80 and ::443 for your proxy or web server, or remove the AAAA record until IPv6 is ready. Let’s Encrypt prefers IPv6 when both are present.
- Redirects without a listener: If you force all HTTP to HTTPS but there is no service on 443 or 443 is blocked, HTTP-01 fails. Bring up 443 first or ease the redirect during issuance.
- CDN or WAF interference: If your CDN does not pass
/.well-known/acme-challenge/through on port 80 exactly as requested, switch to DNS-01 with a scoped API token. This avoids brittle edge behaviour. - CAA blocks issuance: If you publish CAA, make sure
issue "letsencrypt.org"is present. For wildcard issuance add the correspondingissuewild. Without this, renewals will fail. - Hit Let’s Encrypt rate limits: Back off. Do not script repeated re-issues. Renewals are generally exempt and ARI helps spread load, but the main limit of 50 certificates per registered domain per 7 days still applies to new issuance and duplicates.
- Lost ACME state in containers: Persist Caddy’s storage, Traefik’s
acme.jsonand Certbot’s/etc/letsencryptand/var/lib/letsencrypt. Without persistence, your service will try to re-issue on each start and can be throttled. - Edited Certbot files by hand: Do not hand-edit files in
/etc/letsencrypt. Use documented Certbot commands. Dry-run before changing renewal parameters. - Firewall surprises with Docker: Docker manipulates iptables and can bypass expectations set with UFW alone. Place your allow or deny rules in the DOCKER-USER chain or equivalent so they apply to published container ports.
What next
If you are building this on one of our VPS plans and have not picked a server yet, have a look at our Linux VPS. London is the product name and network. The servers are in our own data centre in Maidenhead, Berkshire.
For more on server setup, browse our VPS guides. If you get stuck with DNS at your registrar or with a certificate that will not issue after you have opened ports 80 and 443, open a support ticket and we will take a look with you.
The next step in this playbook is routing to your containers through a reverse proxy.
Common questions
Can I use a CNAME at the root of my domain?
No. The zone apex already has SOA and NS records. A CNAME cannot coexist with other data at the same name. Use A and AAAA at the apex. Point www to the apex with a CNAME if you like.
Do I need IPv6 for Let’s Encrypt to work?
No, but if you publish an AAAA record Let’s Encrypt will prefer IPv6. If you add AAAA, make sure something is listening on ports 80 and 443 on IPv6. If not, remove the AAAA until you are ready.
Port 80 is blocked where I am. Can I still get a certificate?
Yes, by using DNS-01 with a DNS plugin. Certbot does not support TLS-ALPN-01 at this time, and Let’s Encrypt recommends keeping port 80 open. If 80 is blocked by an ISP or policy you cannot change, use DNS-01. Many reverse proxies also support DNS-01.
How do I know renewals are really happening?
With Certbot, run a dry-run using certbot renew --dry-run, check the systemd timer is present with systemctl list-timers, and read recent logs with journalctl. From outside, verify the served certificate’s expiry with the OpenSSL command shown above. With Caddy or Traefik, confirm their logs show periodic renewals and that their storage is persistent.
I set CAA records. What do I need for Let’s Encrypt?
Include a record that authorises Let’s Encrypt to issue. For example: 0 issue "letsencrypt.org". For wildcards, add issuewild for letsencrypt.org as well. Without that, issuance and renewals will fail.