SSL Certificates for Self-Hosting
What Are SSL Certificates?
SSL certificates (technically TLS certificates — SSL is the deprecated predecessor, but everyone still says “SSL”) encrypt the connection between a browser and your server. When you see the padlock icon and https:// in the address bar, a certificate is doing the work.
For self-hosting, SSL certificates matter for three reasons: browsers block or warn on HTTP connections to non-localhost addresses, encrypted connections prevent anyone on your network from sniffing passwords and data, and some self-hosted apps require HTTPS to function (Progressive Web Apps, WebAuthn, clipboard access).
Prerequisites
- A Linux server with Docker installed (Getting Started)
- A domain name pointing to your server (DNS Explained)
- A reverse proxy (Nginx Proxy Manager, Traefik, or Caddy)
How TLS Works (Simplified)
- Browser connects to
https://photos.example.com - Server presents its certificate — proves it is
photos.example.com - Browser verifies the certificate was signed by a trusted Certificate Authority (CA)
- Key exchange — browser and server agree on an encryption key
- Encrypted communication — all data between browser and server is encrypted
Certificates expire (typically 90 days for Let’s Encrypt) and must be renewed. Most reverse proxies handle renewal automatically.
Getting Free Certificates with Let’s Encrypt
Let’s Encrypt is a free, automated Certificate Authority. It issues certificates valid for 90 days and supports automatic renewal. Nearly every self-hosting reverse proxy integrates with it.
Challenge Types
Let’s Encrypt needs to verify you control the domain before issuing a certificate. Two methods:
HTTP-01 Challenge (simplest)
Let’s Encrypt makes an HTTP request to http://yourdomain.com/.well-known/acme-challenge/[token]. Your server must respond with the correct token. This requires port 80 to be open and reachable from the internet.
Internet → Port 80 → Reverse Proxy → Serves challenge token → Certificate issued
DNS-01 Challenge (most flexible)
You create a TXT record _acme-challenge.yourdomain.com with a specific value. No port forwarding needed. Works for wildcard certificates (*.example.com). Requires API access to your DNS provider.
Let's Encrypt asks: "Add TXT record _acme-challenge.example.com = [token]"
Your DNS API adds the record → Let's Encrypt verifies → Certificate issued
The recommendation: Use DNS-01 if your domain is on Cloudflare (most self-hosters use Cloudflare). It supports wildcards, works behind CGNAT, and does not require opening port 80. Use HTTP-01 if your DNS provider does not have API support.
Setting Up SSL with Common Reverse Proxies
Nginx Proxy Manager
Nginx Proxy Manager handles Let’s Encrypt certificates through its web UI with zero command-line work.
- Add a proxy host for your service
- In the SSL tab, select “Request a new SSL Certificate”
- Check “Force SSL” and “HTTP/2 Support”
- Enter your email for Let’s Encrypt notifications
- Click Save — the certificate is issued and auto-renews
For DNS-01 challenges (wildcards, no port 80):
- In the SSL tab, select “Use a DNS Challenge”
- Choose your DNS provider (Cloudflare, etc.)
- Enter your API credentials
See the full Nginx Proxy Manager guide for Docker setup.
Traefik
Traefik automates certificate management through its configuration. Add to your traefik.yml:
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /letsencrypt/acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
Set the Cloudflare API token in your environment:
environment:
- CF_DNS_API_TOKEN=your-cloudflare-api-token
Then label your containers to request certificates:
labels:
- "traefik.http.routers.photos.tls=true"
- "traefik.http.routers.photos.tls.certresolver=letsencrypt"
- "traefik.http.routers.photos.rule=Host(`photos.example.com`)"
Traefik handles issuance and renewal automatically. See the full Traefik guide for complete setup.
Caddy
Caddy obtains and renews certificates automatically with zero configuration. Just specify a domain:
photos.example.com {
reverse_proxy immich:2283
}
files.example.com {
reverse_proxy nextcloud:80
}
Caddy detects the domain names, obtains Let’s Encrypt certificates via HTTP-01, and renews them automatically. For DNS-01 challenges, use the Cloudflare plugin:
photos.example.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
reverse_proxy immich:2283
}
Caddy is the simplest option for automatic HTTPS. See the full Caddy guide.
Wildcard Certificates
A wildcard certificate covers *.example.com — one certificate for photos.example.com, files.example.com, music.example.com, and every other subdomain. This is cleaner than issuing individual certificates and avoids hitting Let’s Encrypt rate limits.
Requirements:
- DNS-01 challenge (wildcards cannot use HTTP-01)
- API access to your DNS provider
Let’s Encrypt rate limits:
- 50 certificates per registered domain per week
- 5 duplicate certificates per week
- 300 new orders per account per 3 hours
For most self-hosters, a single wildcard certificate is sufficient and avoids all rate limit concerns.
Self-Signed Certificates (Local Only)
If you do not have a public domain or cannot use Let’s Encrypt, you can generate self-signed certificates. Browsers will show a security warning, but the connection is still encrypted.
# Generate a self-signed certificate valid for 365 days
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/selfsigned.key \
-out /etc/ssl/certs/selfsigned.crt \
-subj "/CN=192.168.1.50"
To avoid browser warnings, add the generated CA certificate to your devices’ trust stores. This is manual and annoying — a real domain with Let’s Encrypt is almost always the better option.
Alternative: mkcert for local development:
# Install mkcert
sudo apt install libnss3-tools
curl -L https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64 -o mkcert
chmod +x mkcert
sudo mv mkcert /usr/local/bin/
# Install the local CA
mkcert -install
# Generate certificates for local domains
mkcert "photos.home" "files.home" "*.home"
mkcert creates a local Certificate Authority and adds it to your system trust store. Browsers on that machine trust the certificates with no warnings. You still need to install the CA on every device that accesses the services.
Common Mistakes
Not opening port 80 for HTTP-01 challenges
Let’s Encrypt’s HTTP-01 challenge requires port 80 to be reachable from the internet. If your router does not forward port 80 to your server, the challenge fails. Switch to DNS-01 if you cannot open port 80 (common with ISPs that block it).
Forgetting to renew certificates
Let’s Encrypt certificates expire after 90 days. If your reverse proxy is not auto-renewing, services break silently. Nginx Proxy Manager, Traefik, and Caddy all auto-renew by default — verify renewal is working by checking logs after 60 days.
Using SSL termination incorrectly
Your reverse proxy should terminate SSL (handle the certificate), then forward traffic to containers over plain HTTP on the internal Docker network. Do not try to configure certificates inside individual containers — one certificate at the reverse proxy covers everything.
Rate limiting during testing
If you issue too many test certificates, Let’s Encrypt temporarily blocks your domain. Use the staging environment for testing:
# Let's Encrypt staging URL (for testing — certificates won't be trusted)
https://acme-staging-v02.api.letsencrypt.org/directory
Running services on HTTP after setting up HTTPS
After enabling HTTPS, force all HTTP traffic to redirect to HTTPS. Most reverse proxies have a “Force SSL” option. Users typing http://photos.example.com should automatically redirect to https://.
Next Steps
- Set up a reverse proxy if you have not already
- Configure DNS for your domain
- Secure SSH access with key-based authentication
- Set up a firewall to control network access
- Learn about port forwarding for external access
FAQ
Do I need SSL for services only accessed on my local network?
Strictly speaking, no — your LAN traffic is not passing through the public internet. But HTTPS prevents snooping from other devices on your network, enables features that require secure contexts (PWAs, WebAuthn), and eliminates browser warnings. Use it if you can.
Can I get a wildcard certificate from Let’s Encrypt?
Yes. Wildcard certificates require the DNS-01 challenge. Your DNS provider must have an API that your ACME client (reverse proxy) supports. Cloudflare, DigitalOcean, Route53, and most major providers are supported.
What happens when my certificate expires?
Browsers show a full-page security warning and refuse to connect (or require explicit override). Your services still work — only the HTTPS encryption breaks. Auto-renewal from your reverse proxy prevents this.
Should I use Cloudflare’s Origin Certificates instead of Let’s Encrypt?
Cloudflare Origin Certificates only work when traffic passes through Cloudflare’s proxy. They last 15 years, so no renewal needed. Use them if you always access services through Cloudflare’s proxy. Use Let’s Encrypt if you need certificates that work without Cloudflare.
Related
Get self-hosting tips in your inbox
New guides, comparisons, and setup tutorials — delivered weekly. No spam.