I have a basic docker compose file with two services: traefik and nginx
services:
traefik:
image: traefik:v3.2
command:
# - "--api.debug=true"
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.file.directory=/etc/traefik"
- "--providers.file.watch=true"
- "--entryPoints.web.address=:80"
- "--entryPoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.email=my@email.com"
- "--certificatesresolvers.myresolver.acme.storage=/etc/traefik/acme.json"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
# - "--serverstransport.insecureskipverify"
depends_on:
- nginx
volumes:
- ./traefik:/etc/traefik
ports:
- "80:80"
- "443:443"
- "8080:8080"
restart: "unless-stopped"
nginx:
image: nginx:latest
ports:
- "80"
volumes:
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/conf/conf.d:/etc/nginx/conf.d
- ./nginx/data:/usr/share/nginx
restart: "unless-stopped"
my traefik dynamic config is the following:
http:
routers:
nginx:
entrypoints:
- web
- websecure
rule: "Host(`www.host1.com`) || Host(`www.host2.com`) || Host(`host1.com`) || Host(`host2.com`)"
service: nginx
middlewares:
redirect-to-https:
redirectScheme:
permanent: true
scheme: https
services:
nginx:
loadBalancer:
servers:
- url: http://nginx/
When i spin up the compose environment, I see an empty acme.json
file created under ./traefik
. I cannot make it generate a certificate (here using the staging acme server, but same happens with prod).
root@cloud:~/cloud# docker compose up
[+] Running 3/1
✔ Network cloud_default Created 0.1s
✔ Container cloud-nginx-1 Created 0.0s
✔ Container cloud-traefik-1 Created 0.0s
Attaching to nginx-1, traefik-1
nginx-1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
nginx-1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
nginx-1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
nginx-1 | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
nginx-1 | 10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf differs from the packaged version
nginx-1 | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
nginx-1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
nginx-1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
nginx-1 | /docker-entrypoint.sh: Configuration complete; ready for start up
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: using the "epoll" event method
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: nginx/1.25.4
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: OS: Linux 5.15.0-125-generic
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: start worker processes
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: start worker process 29
nginx-1 | 2024/11/11 12:00:56 [notice] 1#1: start worker process 30
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/cmd/traefik/traefik.go:103 > Traefik version 3.2.0 built on 2024-10-28T14:49:00Z version=3.2.0
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/cmd/traefik/traefik.go:110 > Static configuration loaded [json] staticConfiguration={"api":{"dashboard":true,"insecure":true},"certificatesResolvers":{"myresolver":{"acme":{"caServer":"https://acme-staging-v02.api.letsencrypt.org/directory","certificatesDuration":2160,"email":"my@email.com","httpChallenge":{"entryPoint":"web"},"keyType":"RSA4096","storage":"/etc/traefik/acme.json"}}},"entryPoints":{"traefik":{"address":":8080","forwardedHeaders":{},"http":{"maxHeaderBytes":1048576},"http2":{"maxConcurrentStreams":250},"transport":{"lifeCycle":{"graceTimeOut":"10s"},"respondingTimeouts":{"idleTimeout":"3m0s","readTimeout":"1m0s"}},"udp":{"timeout":"3s"}},"web":{"address":":80","forwardedHeaders":{},"http":{"maxHeaderBytes":1048576},"http2":{"maxConcurrentStreams":250},"transport":{"lifeCycle":{"graceTimeOut":"10s"},"respondingTimeouts":{"idleTimeout":"3m0s","readTimeout":"1m0s"}},"udp":{"timeout":"3s"}},"websecure":{"address":":443","forwardedHeaders":{},"http":{"maxHeaderBytes":1048576},"http2":{"maxConcurrentStreams":250},"transport":{"lifeCycle":{"graceTimeOut":"10s"},"respondingTimeouts":{"idleTimeout":"3m0s","readTimeout":"1m0s"}},"udp":{"timeout":"3s"}}},"global":{"checkNewVersion":true},"log":{"format":"common","level":"DEBUG"},"providers":{"file":{"directory":"/etc/traefik","watch":true},"providersThrottleDuration":"2s"},"serversTransport":{"maxIdleConnsPerHost":200},"tcpServersTransport":{"dialKeepAlive":"15s","dialTimeout":"30s"}}
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/cmd/traefik/traefik.go:626 >
traefik-1 | Stats collection is disabled.
traefik-1 | Help us improve Traefik by turning this feature on :)
traefik-1 | More details on: https://doc.traefik.io/traefik/contributing/data-collection/
traefik-1 |
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:73 > Starting provider aggregator aggregator.ProviderAggregator
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/server_entrypoint_tcp.go:231 > Starting TCP Server entryPointName=web
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/server_entrypoint_tcp.go:231 > Starting TCP Server entryPointName=websecure
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/server_entrypoint_tcp.go:231 > Starting TCP Server entryPointName=traefik
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:202 > Starting provider *file.Provider
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:203 > *file.Provider provider configuration config={"directory":"/etc/traefik","watch":true}
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/file/file.go:122 > add watcher on: /etc/traefik
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/file/file.go:122 > add watcher on: /etc/traefik/.gitignore
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/file/file.go:122 > add watcher on: /etc/traefik/acme.json
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/file/file.go:122 > add watcher on: /etc/traefik/base.yml
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:202 > Starting provider *traefik.Provider
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:203 > *traefik.Provider provider configuration config={}
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:202 > Starting provider *acme.Provider
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:203 > *acme.Provider provider configuration config={"HTTPChallengeProvider":{},"ResolverName":"myresolver","TLSChallengeProvider":{},"caServer":"https://acme-staging-v02.api.letsencrypt.org/directory","certificatesDuration":2160,"email":"my@email.com","httpChallenge":{"entryPoint":"web"},"keyType":"RSA4096","storage":"/etc/traefik/acme.json","store":{}}
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/acme/provider.go:221 > Attempt to renew certificates "720h0m0s" before expiry and check every "24h0m0s" acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory providerName=myresolver.acme
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/pkg/provider/acme/provider.go:859 > Testing certificate renew... acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory providerName=myresolver.acme
traefik-1 | 2024-11-11T12:00:56Z INF github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:202 > Starting provider *acme.ChallengeTLSALPN
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/provider/aggregator/aggregator.go:203 > *acme.ChallengeTLSALPN provider configuration config={}
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:227 > Configuration received config={"http":{"middlewares":{"redirect-to-https":{"redirectScheme":{"permanent":true,"scheme":"https"}}},"routers":{"nginx":{"entryPoints":["web","websecure"],"rule":"Host(`www.host1.com`) || Host(`www.host2.com`) || Host(`host1.com`) || Host(`host2.com`)","service":"nginx"}},"services":{"nginx":{"loadBalancer":{"passHostHeader":true,"responseForwarding":{"flushInterval":"100ms"},"servers":[{"url":"http://nginx/"}]}}}},"tcp":{},"tls":{},"udp":{}} providerName=file
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:227 > Configuration received config={"http":{"middlewares":{"dashboard_redirect":{"redirectRegex":{"permanent":true,"regex":"^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$","replacement":"${1}/dashboard/"}},"dashboard_stripprefix":{"stripPrefix":{"prefixes":["/dashboard/","/dashboard"]}}},"routers":{"acme-http":{"entryPoints":["web"],"priority":9223372036854775807,"rule":"PathPrefix(`/.well-known/acme-challenge/`)","ruleSyntax":"v3","service":"acme-http@internal"},"api":{"entryPoints":["traefik"],"priority":9223372036854775806,"rule":"PathPrefix(`/api`)","ruleSyntax":"v3","service":"api@internal"},"dashboard":{"entryPoints":["traefik"],"middlewares":["dashboard_redirect@internal","dashboard_stripprefix@internal"],"priority":9223372036854775805,"rule":"PathPrefix(`/`)","ruleSyntax":"v3","service":"dashboard@internal"}},"serversTransports":{"default":{"maxIdleConnsPerHost":200}},"services":{"acme-http":{},"api":{},"dashboard":{},"noop":{}}},"tcp":{"serversTransports":{"default":{"dialKeepAlive":"15s","dialTimeout":"30s"}}},"tls":{},"udp":{}} providerName=internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:227 > Configuration received config={"http":{},"tcp":{},"tls":{},"udp":{}} providerName=myresolver.acme
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:321 > No default certificate, fallback to the internal generated certificate tlsStoreName=default
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/service/service.go:299 > Creating load-balancer entryPointName=web routerName=nginx@file serviceName=nginx@file
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/service/service.go:336 > Creating server entryPointName=web routerName=nginx@file serverName=70a994c85944ea66 serviceName=nginx@file target=http://nginx/
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:25 > Creating middleware entryPointName=web middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:25 > Creating middleware entryPointName=websecure middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/stripprefix/strip_prefix.go:32 > Creating middleware entryPointName=traefik middlewareName=dashboard_stripprefix@internal middlewareType=StripPrefix routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/observability/middleware.go:33 > Adding tracing to middleware entryPointName=traefik middlewareName=dashboard_stripprefix@internal routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/redirect/redirect_regex.go:17 > Creating middleware entryPointName=traefik middlewareName=dashboard_redirect@internal middlewareType=RedirectRegex routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/redirect/redirect_regex.go:18 > Setting up redirection from ^(http:\/\/(\[[\w:.]+\]|[\w\._-]+)(:\d+)?)\/$ to ${1}/dashboard/ entryPointName=traefik middlewareName=dashboard_redirect@internal middlewareType=RedirectRegex routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/observability/middleware.go:33 > Adding tracing to middleware entryPointName=traefik middlewareName=dashboard_redirect@internal routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:25 > Creating middleware entryPointName=traefik middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:321 > No default certificate, fallback to the internal generated certificate tlsStoreName=default
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/service/service.go:299 > Creating load-balancer entryPointName=websecure routerName=nginx@file serviceName=nginx@file
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/service/service.go:336 > Creating server entryPointName=websecure routerName=nginx@file serverName=70a994c85944ea66 serviceName=nginx@file target=http://nginx/
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:25 > Creating middleware entryPointName=websecure middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/stripprefix/strip_prefix.go:32 > Creating middleware entryPointName=traefik middlewareName=dashboard_stripprefix@internal middlewareType=StripPrefix routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/observability/middleware.go:33 > Adding tracing to middleware entryPointName=traefik middlewareName=dashboard_stripprefix@internal routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/redirect/redirect_regex.go:17 > Creating middleware entryPointName=traefik middlewareName=dashboard_redirect@internal middlewareType=RedirectRegex routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/redirect/redirect_regex.go:18 > Setting up redirection from ^(http:\/\/(\[[\w:.]+\]|[\w\._-]+)(:\d+)?)\/$ to ${1}/dashboard/ entryPointName=traefik middlewareName=dashboard_redirect@internal middlewareType=RedirectRegex routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/observability/middleware.go:33 > Adding tracing to middleware entryPointName=traefik middlewareName=dashboard_redirect@internal routerName=dashboard@internal
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:25 > Creating middleware entryPointName=traefik middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:25 > Creating middleware entryPointName=web middlewareName=traefik-internal-recovery middlewareType=Recovery
DNS resolution works correctly for all the domains used in the config: i.e. all points to the same IP address, that of the server where this is deployed.
Why isn't traefik receiving the certificate?
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:227 > Configuration received config={"http":{},"tcp":{},"tls":{},"udp":{}} providerName=myresolver.acme
traefik-1 | 2024-11-11T12:00:56Z DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:321 > No default certificate, fallback to the internal generated certificate tlsStoreName=default
You need to define the certificate resolver also in the dynamic config for each router, like this:
http:
routers:
nginx:
entrypoints:
- web
- websecure
rule: "Host(`www.host1.com`) || Host(`www.host2.com`) || Host(`host1.com`) || Host(`host2.com`)"
service: nginx
tls:
certResolver: myresolver
Or alternatively in the service labels in docker compose:
nginx:
image: nginx:latest
...
labels:
- traefik.http.routers.nginx.tls=true
- traefik.http.routers.nginx.tls.certresolver=myresolver
but since you already do most of the stuff in the dynamic config, having it defined there would make more sense.
Be sure to revisit the traefik documentation example.