HomeLab SSL with Traefik v3 and Local CA
/ 7 min read
Table of Contents
HomeLab SSL Made Easy: Traefik v3 & Local CA Guide
Tired of seeing “Your connection is not private” every time you access your local dashboard? While services like Let’s Encrypt are great for public-facing sites, securing internal-only services requires a different approach.
In this guide, we will create a private Certificate Authority (CA) and configure Traefik v3 to serve a wildcard certificate for your entire local domain (e.g., *.homelab.local).
📦 Complete Working Example: All the scripts and configuration files from this guide are available in the traefik-ssl-demo repository. Clone it and get started in minutes!
Prerequisites
- Docker and Docker Compose installed
- Basic understanding of DNS and certificates
- A local DNS server (AdGuard Home, Pi-hole, or similar)
- If your router has a built-in DNS server, you can use it instead of setting up a separate one.
- You can also use host file entries on your devices to point to your local DNS server. If you’re using macOS, you can edit
/etc/hoststo add entries like192.168.1.100 dashboard.homelab.local. For Windows, editC:\Windows\System32\drivers\etc\hosts. It must be done on each device that will access your local services.
- Local domain (we’ll use
homelab.localin this guide)
Step 1: Create your Local Certificate Authority
First, we generate a Root CA. Once your devices trust this root, they will trust any certificate signed by it.
# Create directory structuremkdir -p ~/local-ca/{certs,private}
# Secure the private directorychmod 700 ~/local-ca/private
# Generate CA private key (4096 bits for better security)openssl genrsa -out ~/local-ca/private/ca.key 4096
# Secure the private keychmod 600 ~/local-ca/private/ca.key
# Generate CA certificate (valid for 10 years)openssl req -x509 -new -nodes -key ~/local-ca/private/ca.key \ -sha256 -days 3650 -out ~/local-ca/certs/ca.crt \ -subj "/C=ES/ST=State/L=City/O=HomeLab/OU=IT/CN=HomeLab Root CA"Security Note: The CA private key is the most sensitive file. Keep it secure and never share it!
Step 2: Generate a Wildcard Certificate
We’ll create one wildcard certificate for *.homelab.local to cover all current and future services.
1. Create the private key and CSR
# Change to the CA directorycd ~/local-ca
# Generate 4096-bit private key (matching CA strength)openssl genrsa -out wildcard-homelab.key 4096
# Secure the keychmod 600 wildcard-homelab.key
# Create Certificate Signing Requestopenssl req -new -key wildcard-homelab.key -out wildcard-homelab.csr \ -subj "/C=ES/ST=State/L=City/O=HomeLab/OU=IT/CN=*.homelab.local"2. Define Subject Alternative Names (SAN)
Modern browsers require SAN fields to validate certificates properly.
cat > wildcard-homelab.ext << 'EOF'authorityKeyIdentifier=keyid,issuerbasicConstraints=CA:FALSEkeyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEnciphermentextendedKeyUsage = serverAuthsubjectAltName = @alt_names
[alt_names]DNS.1 = *.homelab.localDNS.2 = homelab.localEOF3. Sign the certificate
openssl x509 -req -in wildcard-homelab.csr \ -CA ~/local-ca/certs/ca.crt \ -CAkey ~/local-ca/private/ca.key \ -CAcreateserial \ -out wildcard-homelab.crt \ -days 730 \ -sha256 \ -extfile wildcard-homelab.ext4. Verify the certificate
# Check certificate detailsopenssl x509 -in wildcard-homelab.crt -text -noout | grep -A1 "Subject Alternative Name"
# Verify certificate against CAopenssl verify -CAfile ~/local-ca/certs/ca.crt wildcard-homelab.crtYou should see your SANs and a “OK” verification message.
Step 3: Traefik v3 Setup with Security Best Practices
Directory Structure
Create the following structure for Traefik:
mkdir -p stacks/traefik/{configs,certs}cd stacks/traefik
# Copy certificatescp ~/local-ca/wildcard-homelab.{crt,key} ./certs/chmod 600 ./certs/wildcard-homelab.key1. TLS Configuration File
Path: stacks/traefik/configs/tls.yml
tls: certificates: - certFile: /certs/wildcard-homelab.crt keyFile: /certs/wildcard-homelab.key
stores: default: defaultCertificate: certFile: /certs/wildcard-homelab.crt keyFile: /certs/wildcard-homelab.key2. Middlewares Configuration
Path: stacks/traefik/configs/middlewares.yml
http: middlewares: # Security headers security-headers: headers: sslRedirect: true forceSTSHeader: true stsSeconds: 31536000 stsIncludeSubdomains: true stsPreload: true contentTypeNosniff: true browserXssFilter: true referrerPolicy: "strict-origin-when-cross-origin" customFrameOptionsValue: "SAMEORIGIN"
# HTTPS redirect https-redirect: redirectScheme: scheme: https permanent: true3. Docker Compose Setup
Path: stacks/traefik/docker-compose.yml
services: traefik: image: traefik:v3.6 container_name: traefik restart: unless-stopped security_opt: - no-new-privileges:true command: # Docker provider - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--providers.docker.network=traefik_proxy"
# File provider for configs - "--providers.file.directory=/configs/" - "--providers.file.watch=true"
# Entry points - "--entrypoints.web.address=:80" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--entrypoints.websecure.address=:443" - "--entrypoints.websecure.http.tls=true"
# API and dashboard (optional but recommended) - "--api.dashboard=true" - "--api.insecure=false"
# Logging - "--log.level=INFO" - "--accesslog=true"
ports: - "80:80" - "443:443"
volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./configs:/configs:ro - ./certs:/certs:ro
networks: - traefik_proxy
labels: # Enable Traefik for itself (dashboard) - "traefik.enable=true"
# Dashboard router - "traefik.http.routers.dashboard.rule=Host(`traefik.homelab.local`)" - "traefik.http.routers.dashboard.service=api@internal" - "traefik.http.routers.dashboard.middlewares=security-headers@file"
# Dummy service (dashboard doesn't need one, but Docker provider requires it) - "traefik.http.services.dashboard.loadbalancer.server.port=8080"
networks: traefik_proxy: name: traefik_proxy driver: bridgeStep 4: Example Service Configuration
Here’s a complete example of a service behind Traefik:
services: whoami: image: traefik/whoami container_name: whoami restart: unless-stopped networks: - traefik_proxy labels: - "traefik.enable=true" - "traefik.http.routers.whoami.rule=Host(`whoami.homelab.local`)" - "traefik.http.routers.whoami.entrypoints=websecure" - "traefik.http.routers.whoami.tls=true" - "traefik.http.routers.whoami.middlewares=security-headers@file" - "traefik.http.services.whoami.loadbalancer.server.port=80"
networks: traefik_proxy: external: trueStep 5: Trust the CA on your Devices
Install the ca.crt file as a Trusted Root on all your devices to enable the “Green Lock”.
Windows
- Right-click
ca.crt→ Install Certificate - Select Local Machine → Next
- Choose Place all certificates in the following store → Browse
- Select Trusted Root Certification Authorities → OK
- Next → Finish
macOS
- Open Keychain Access
- Drag
ca.crtinto the System keychain - Double-click the certificate
- Expand Trust section
- Set When using this certificate to Always Trust
- Close and enter your password
Linux (Ubuntu/Debian)
sudo cp ~/local-ca/certs/ca.crt /usr/local/share/ca-certificates/homelab.crtsudo update-ca-certificatesFirefox (All Platforms)
Firefox uses its own certificate store and doesn’t use the system certificates.
- Open Firefox → Settings → Privacy & Security
- Scroll to Certificates → View Certificates
- Go to Authorities tab → Import
- Select your
ca.crtfile - Check Trust this CA to identify websites
- Click OK
Step 6: DNS Configuration
In your DNS server (AdGuard Home, Pi-hole, etc.), create DNS rewrites:
AdGuard Home
Go to Filters → DNS rewrites and add:
*.homelab.local → 192.168.1.X (Traefik IP)Pi-hole
Add to /etc/dnsmasq.d/02-custom.conf:
address=/homelab.local/192.168.1.XThen restart: sudo systemctl restart pihole-FTL
Host file
Add to /etc/hosts:
192.168.1.X service.homelab.localTroubleshooting
Certificate Not Trusted
Problem: Browser still shows “Not Secure”
Solutions:
- Verify CA is installed in correct store (System, not User)
- Clear browser cache and restart browser
- Check certificate chain:
openssl s_client -connect service.homelab.local:443 -showcerts - For Firefox, ensure CA is imported in Firefox’s certificate manager
DNS Not Resolving
Problem: Can’t reach service.homelab.local
Solutions:
- Check DNS server:
nslookup service.homelab.local - Verify DNS rewrite rule is active
- Clear DNS cache:
sudo systemd-resolve --flush-caches(Linux) oripconfig /flushdns(Windows) - Ensure your device uses your local DNS server
Traefik Not Routing
Problem: Gets certificate but shows 404
Solutions:
- Check Traefik logs:
docker logs traefik - Verify service labels are correct
- Ensure service is on
traefik_proxynetwork - Check Traefik dashboard at
https://traefik.homelab.local
Mixed Content Warnings
Problem: Site loads but some resources are HTTP
Solution: Ensure all internal URLs use HTTPS or relative paths
Certificate Renewal
Your wildcard certificate is valid for 2 years (730 days). To renew:
# 1. Create new CSR with same keycd ~/local-caopenssl req -new -key wildcard-homelab.key -out wildcard-homelab.csr \ -subj "/C=ES/ST=State/L=City/O=HomeLab/OU=IT/CN=*.homelab.local"
# 2. Sign with CA againopenssl x509 -req -in wildcard-homelab.csr \ -CA ~/local-ca/certs/ca.crt \ -CAkey ~/local-ca/private/ca.key \ -CAcreateserial \ -out wildcard-homelab.crt \ -days 730 \ -sha256 \ -extfile wildcard-homelab.ext
# 3. Copy to Traefik and reloadcp wildcard-homelab.crt stacks/traefik/certs/docker restart traefikPro Tip: Set a calendar reminder 1 month before expiration!
Security Considerations
✅ Best Practices
- Never share your CA private key - It can sign certificates for any domain
- Use strong passwords - Protect any system storing the CA key
- Rotate certificates regularly - Don’t wait until expiration
- Limit CA usage - Only use it for internal services
- Backup securely - Keep encrypted backups of your CA
⚠️ Limitations
- Not for public internet - This setup is for internal networks only. Don’t use it on production environments.
- Trust is manual - Each device must manually trust your CA
- No automatic renewal - Unlike Let’s Encrypt, this is manual
- Certificate validation - External tools won’t recognize your CA
Conclusion
You now have a production-ready HomeLab SSL setup with:
- ✅ Custom Certificate Authority with proper security
- ✅ 4096-bit encryption for both CA and certificates
- ✅ Wildcard SSL certificate covering all services
- ✅ Traefik v3 with automatic HTTPS redirection
- ✅ Security headers for enhanced protection
- ✅ Working example with dashboard access
- ✅ Multi-platform support including Firefox
- ✅ Troubleshooting guide for common issues
This setup provides enterprise-grade security for your internal services while maintaining the convenience of a single wildcard certificate. Your HomeLab now has the same level of SSL protection as production environments!
Next Steps
- Add more services to your
docker-compose.yml - Explore Traefik middlewares (authentication, rate limiting, etc.)
- Set up monitoring for certificate expiration
- Consider automating certificate renewal with scripts
Happy HomeLab-ing! 🚀🔒