Infrastructure guide
Traefik Docker Compose with HTTPS: A Production-Safe Starting Point
Deploy Traefik with opt-in service discovery, automatic HTTPS, persistent ACME state, a protected dashboard, and practical 404/502 diagnostics.
Published and reviewed by OpenAlt · September 25, 2026

TL;DR
This traefik docker compose baseline routes only services that explicitly opt in, redirects HTTP to HTTPS, obtains certificates through ACME, and keeps the dashboard behind authentication without publishing port 8080. It uses ports 80 and 443, a shared Docker network, read-only Docker socket access, persistent certificate storage, access logs, and Prometheus metrics.
The important safety defaults are providers.docker.exposedbydefault=false, a pinned Traefik image tag, and no --api.insecure=true. The official Docker quick start labels that insecure API mode as development-only: Traefik Docker quick start. The production-oriented configuration pattern is documented in Traefik’s Docker setup guide.
TOC
- Network and DNS preflight
- Secure Compose baseline
- Persistent ACME storage permissions
- First app labels
- Dashboard authentication
- Verify redirect and certificate
- Backup, upgrade, and rollback
- Troubleshooting
- FAQ
Network and DNS preflight
Traefik needs public reachability on TCP ports 80 and 443. Point each routed hostname to the server’s public address with DNS A and, when used, AAAA records. If IPv6 is published, verify that the host and firewall accept IPv6 traffic too; an incorrect AAAA record can make a hostname appear intermittently unavailable.
The HTTP-01 ACME challenge requires port 80 to reach Traefik. Do not block that port simply because normal application traffic will be redirected to HTTPS. The redirect and certificate challenge can coexist when Traefik owns the web entrypoint.
Create one external Docker network named proxy. Traefik and every routed application must join it. The network lets Traefik reach containers by Docker service metadata while keeping unrelated containers outside the routing path.
Secure Compose baseline
Save this as compose.yml. The example uses reserved documentation hostnames; replace them with DNS names that resolve to this server before issuing public certificates.
services:
traefik:
image: traefik:v3.4
restart: unless-stopped
command:
- --api.dashboard=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=proxy
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --certificatesresolvers.letsencrypt.acme.email=ops@example.net
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
- --accesslog=true
- --metrics.prometheus=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- ./auth:/auth:ro
networks:
- proxy
networks:
proxy:
name: proxy
external: true
The Docker socket is mounted read-only, which prevents Traefik from writing through that mount. It does not make Docker discovery risk-free: Traefik can still inspect container metadata, labels, networks, and other information exposed by the socket. Treat the socket as sensitive, limit who can modify Docker workloads, and keep the host protected.
The ACME resolver stores certificates and account data in /letsencrypt/acme.json. Traefik’s official ACME documentation explains certificate resolvers and persistent storage.
Persistent ACME storage permissions
Create the external network and storage directories before starting the stack:
docker network create proxy
mkdir -p letsencrypt auth
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json
The file must remain writable by the Traefik process and inaccessible to other users. Its contents include certificate material and ACME account information, so do not commit it to a public repository or expose it through a web server. If the container runs with a non-root user in your environment, confirm that the mounted file and directory are writable by that user.
The email address in the resolver should be monitored. It gives the certificate authority a way to communicate about account or certificate events. Certificate storage must persist across container recreation; otherwise Traefik may lose its account state and request certificates unnecessarily.


First app labels
A service is not routed merely because it runs on the host. With exposedbydefault=false, it must carry an explicit enable label and join the proxy network.
whoami:
image: traefik/whoami:v1.10
restart: unless-stopped
networks:
- proxy
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.whoami.rule=Host(`whoami.example.net`)
- traefik.http.routers.whoami.entrypoints=websecure
- traefik.http.routers.whoami.tls.certresolver=letsencrypt
- traefik.http.services.whoami.loadbalancer.server.port=80
The internal service port is 80, even though the application has no published host port. That is the desired pattern: Traefik is the public entrypoint, while Docker networking carries traffic internally.
A routed application can be attached to multiple networks. traefik.docker.network=proxy removes ambiguity and tells Traefik which shared network to use. The application does not need to publish ports to the host.
Dashboard authentication
Do not enable --api.insecure=true. That option exposes the dashboard through port 8080 and is explicitly intended for development. The secure pattern enables the dashboard API internally and creates an HTTPS router for api@internal.
Generate a bcrypt password entry and store it in the mounted file:
htpasswd -nB operator > auth/users
chmod 600 auth/users
Add these labels to the Traefik service:
- traefik.http.routers.dashboard.rule=Host(`traefik.example.net`)
- traefik.http.routers.dashboard.entrypoints=websecure
- traefik.http.routers.dashboard.tls.certresolver=letsencrypt
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.routers.dashboard.middlewares=dashboard-auth
- traefik.http.middlewares.dashboard-auth.basicauth.usersfile=/auth/users
Only ports 80 and 443 are published. Port 8080 is not exposed, and the dashboard is protected by basic authentication before it reaches the internal API service. Use a dedicated account and a strong password; basic authentication should still be carried over HTTPS only.
Verify redirect and certificate
After starting the stack, confirm that Docker reports both containers on the proxy network and inspect Traefik logs for configuration or ACME errors.
Test the expected behavior:
curl -I http://whoami.example.net
curl -vk https://whoami.example.net
The HTTP request should return a redirect to HTTPS. The HTTPS request should complete with a certificate matching the hostname and return the application response. Open the dashboard hostname in a browser and confirm that authentication is required.
If the certificate is not ready immediately, allow time for the ACME request and check logs. Avoid repeated restarts while troubleshooting; persistent storage prevents unnecessary account churn.
Backup, upgrade, and rollback
Back up compose.yml, the auth/users file, and letsencrypt/acme.json. Store the ACME file encrypted and restrict access because it contains private certificate data. Also preserve DNS records and any host firewall configuration needed for ports 80 and 443.
Pin an explicit Traefik version instead of using latest. Before an upgrade, make a backup, review the relevant release notes, pull the new image, and recreate the service during a maintenance window. Keep the previous image tag available so a rollback can restore the last known-good configuration. Test routing, certificate renewal, dashboard authentication, and logs after every upgrade.
Troubleshooting
A 404 usually means Traefik received the request but no router rule matched it. Check the requested hostname, DNS result, entrypoint, HTTPS scheme, and spelling of the Host() rule. Also confirm traefik.enable=true; with opt-in discovery, missing that label intentionally leaves the service unrouted.
A 502 usually means the router matched but Traefik could not reach the backend. Confirm that the application and Traefik share the proxy network, that traefik.docker.network=proxy is correct, and that loadbalancer.server.port matches the application’s listening port. Check whether the application is listening on its container interface rather than only on localhost.
A certificate error commonly comes from DNS pointing elsewhere, port 80 being blocked, an incorrect ACME entrypoint, or an unwritable acme.json. Verify the hostname resolves to this server, inspect Traefik’s ACME logs, and confirm that the storage file remains present with restrictive permissions.
FAQ
Why disable exposed-by-default?
It creates an explicit allowlist. A newly started container does not become publicly reachable just because it has a Docker label or happens to share a network. Routing requires deliberate labels, reducing accidental exposure.
Should port 8080 be public?
No. Do not publish port 8080 for a production dashboard. Route api@internal through HTTPS and protect it with authentication, as in the dashboard configuration above.
What must persist?
Persist /letsencrypt/acme.json, the Compose configuration, and the dashboard credential file. The ACME file is essential for retaining certificate and account state across container recreation.
How do services join Traefik’s network?
Attach Traefik and each routed service to the external Docker network named proxy. A service may have other networks, but traefik.docker.network=proxy identifies the network Traefik should use.
Why can a valid route return 502?
A valid router only proves that the hostname and rule matched. The backend can still be unreachable because of a wrong internal port, missing shared network, incorrect network selection, or an application that is not listening on its container interface.
Once the first service works, explore OpenAlt’s Traefik profile, compare self-hosting platforms, or evaluate one-click self-hosting. Readers comparing the coolify traefik path with a directly managed stack can use those options to choose the right operating model.