Traefik: SSL Certificate Not Generating — Fix
The Problem
Traefik is running and routing traffic, but HTTPS shows a self-signed or default Traefik certificate instead of a valid Let’s Encrypt certificate. The browser shows a security warning. Traefik logs may show ACME errors like acme: error: 403 or unable to generate a certificate.
Updated March 2026: Verified with latest Docker images and configurations.
The Cause
SSL certificate generation fails for several reasons:
- Port 80 is not publicly accessible — Let’s Encrypt HTTP-01 challenge requires port 80 open from the internet
- DNS doesn’t point to your server — the domain must resolve to your server’s public IP
- ACME configuration is missing or wrong — the certificate resolver must be defined in static config AND referenced in route labels
- Certificate storage volume not mounted — Traefik stores certs in
acme.json, which must persist across restarts
The Fix
Method 1: Fix HTTP-01 Challenge Configuration
Ensure your static configuration includes a proper ACME resolver:
services:
traefik:
image: traefik:v3.6.11
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "[email protected]"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--providers.docker=true"
ports:
- "80:80"
- "443:443"
volumes:
- letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
volumes:
letsencrypt:
Then reference the resolver in your service labels:
labels:
- "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
Method 2: Use DNS-01 Challenge (When Port 80 Is Blocked)
If your ISP blocks port 80, use DNS-01 challenge with a supported DNS provider:
command:
- "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
environment:
CF_API_EMAIL: "[email protected]"
CF_DNS_API_TOKEN: "your-cloudflare-api-token"
Method 3: Fix File Permissions on acme.json
The acme.json file must have permissions 600. If it was created with wrong permissions, Traefik silently fails:
# Find the file in the volume
docker exec traefik ls -la /letsencrypt/acme.json
# Fix permissions
docker exec traefik chmod 600 /letsencrypt/acme.json
# Restart Traefik
docker compose restart traefik
Debugging
Check Traefik logs for ACME errors:
docker logs traefik 2>&1 | grep -i "acme\|certificate\|challenge"
Enable debug logging temporarily:
command:
- "--log.level=DEBUG"
Prevention
- Always mount a persistent volume for
/letsencrypt/acme.json - Verify DNS records point to your server before enabling ACME
- Test with Let’s Encrypt staging first:
--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory - Use DNS-01 challenge if you’re behind a NAT or firewall that blocks port 80
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments