Skip to main content

Traefik Docker Compose: Reverse Proxy with HTTPS (2026)

To run Traefik with Docker Compose, start the traefik:v3.7 image with the Docker socket mounted read-only and ports 80 and 443 published. Traefik reads labels on your other containers and routes traffic to them. One label sets the domain (Host(...)), one label turns on Let's Encrypt. Traefik requests and renews the certificate itself. You never edit a Traefik config file to add an app.

This guide sets up Traefik in front of one app, adds HTTPS, then shows how to add more apps. Every file below was tested with Traefik v3.7 and Docker 29.

Comparing proxies first? See Traefik alternatives and Caddy alternatives.

Before you start​

  • A Linux server with Docker and Compose. See Install Docker Compose.
  • A domain with an A record that points to the server. This guide uses example.com.
  • Ports 80 and 443 open in the firewall. Let's Encrypt connects to port 80 to check that you own the domain.

Traefik with Docker Compose and HTTPS​

Create a folder, for example /srv/proxy, and put this compose.yaml in it:

services:
traefik:
image: traefik:v3.7
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.email=you@example.com
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
restart: unless-stopped

whoami:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.whoami.rule=Host(`example.com`)
- traefik.http.routers.whoami.entrypoints=websecure
- traefik.http.routers.whoami.tls.certresolver=letsencrypt
restart: unless-stopped

volumes:
letsencrypt:

Start it:

docker compose up -d
docker compose logs -f traefik

Open https://example.com. The whoami container answers with its hostname and IP. The first request can take a few seconds while Traefik gets the certificate.

What each part does​

SettingWhat it does
providers.docker=trueTraefik watches Docker and reads container labels.
exposedbydefault=falseOnly containers with traefik.enable=true get a route. Without this, every container is public.
entrypoints.web / websecureTraefik listens on 80 and 443.
redirections.entrypoint.to=websecureEvery HTTP request is redirected to HTTPS.
certificatesresolvers.letsencryptA Let's Encrypt client named letsencrypt. It uses the HTTP-01 challenge on port 80.
acme.storage=/letsencrypt/acme.jsonCertificates are stored in the letsencrypt volume. They survive restarts and updates.
docker.sock:roTraefik reads container labels through the Docker API.

The labels on whoami create a router: requests for example.com on port 443 go to this container, with a certificate from the letsencrypt resolver.

The ACME challenge on port 80 still works with the HTTPS redirect. Traefik answers Let's Encrypt before the redirect applies.

Add another app​

Add a service with its own labels. You do not restart Traefik. It sees the new container within a second.

app:
image: ghcr.io/you/your-app:latest
labels:
- traefik.enable=true
- traefik.http.routers.app.rule=Host(`app.example.com`)
- traefik.http.routers.app.entrypoints=websecure
- traefik.http.routers.app.tls.certresolver=letsencrypt
- traefik.http.services.app.loadbalancer.server.port=3000
restart: unless-stopped
  • Each router needs a unique name (whoami, app).
  • loadbalancer.server.port tells Traefik which container port to use. You need it when the image exposes no port or more than one. Without it, you get 404 page not found or Bad Gateway.
  • Do not publish the app's port with ports:. Traefik reaches the app over the Docker network. A published port bypasses the proxy.

Run docker compose up -d and the new domain gets its own certificate.

Apps in other Compose projects​

Containers from another compose.yaml are on a different Docker network, and Traefik cannot reach them. Create one shared network:

docker network create proxy

Attach Traefik to it, and tell Traefik to use it:

services:
traefik:
# ... as above, plus:
command:
# ... existing flags, plus:
- --providers.docker.network=proxy
networks:
- proxy

networks:
proxy:
external: true

In the other project, attach the app to the same network:

services:
app:
image: ghcr.io/you/your-app:latest
labels:
- traefik.enable=true
- traefik.http.routers.app.rule=Host(`app.example.com`)
- traefik.http.routers.app.entrypoints=websecure
- traefik.http.routers.app.tls.certresolver=letsencrypt
networks:
- proxy

networks:
proxy:
external: true

If the app also talks to a database, keep the database off the proxy network. Give the app a second, private network for that. See Docker Compose networks.

The Traefik dashboard​

The dashboard shows every router, service and certificate. It is useful for debugging. Do not make it public. Bind it to localhost:

services:
traefik:
command:
# ... existing flags, plus:
- --api.insecure=true
ports:
- "80:80"
- "443:443"
- "127.0.0.1:8080:8080"

Open it from your laptop through an SSH tunnel:

ssh -L 8080:localhost:8080 you@your-server
# then open http://localhost:8080/dashboard/

Test with the Let's Encrypt staging server​

Let's Encrypt limits how many certificates you can get. Five failed validations per domain per hour is the first limit people hit. While you test, use the staging server. Its certificates are not trusted by browsers, but it has much higher limits:

- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory

When it works, remove the line, delete the stored staging certificates (docker compose down and docker volume rm proxy_letsencrypt; the volume name is <folder>_letsencrypt), and start again.

Troubleshooting​

404 page not found​

Traefik has no router for the request. Check:

  • The container has traefik.enable=true.
  • The Host(...) rule matches the domain exactly. Use backticks, not quotes.
  • The container is on a network Traefik is attached to (see Apps in other Compose projects).
  • The container is running. Traefik removes routes for stopped containers.

The dashboard shows which routers exist.

client version 1.24 is too old​

The full log line is:

Error response from daemon: client version 1.24 is too old. Minimum supported API version is 1.40

Docker 29 removed support for old Docker API versions. Traefik v3.5 and older use API 1.24 and stop seeing containers, so every request returns 404. Update Traefik to v3.6 or newer. We reproduced this with traefik:v3.5 on Docker 29; traefik:v3.6 and v3.7 work.

Bad Gateway​

Traefik found the router but cannot connect to the app. The app listens on a different port (set loadbalancer.server.port), listens only on 127.0.0.1 inside the container (make it listen on 0.0.0.0), or is on a different network.

No certificate, browser shows TRAEFIK DEFAULT CERT​

Traefik could not get a certificate and serves its own self-signed one. Run docker compose logs traefik | grep -i acme. Common causes: the DNS record does not point to this server yet, port 80 is blocked, or you hit a rate limit.

Security note on the Docker socket​

Read-only (:ro) only stops Traefik from replacing the socket file. Any process with access to the socket can still control Docker, which means root on the host. That is normal for Traefik's Docker provider. If you need to limit it, put a socket proxy such as tecnativa/docker-socket-proxy in between and point Traefik at it with --providers.docker.endpoint=tcp://socket-proxy:2375.

Traefik without Docker​

Traefik also runs as a single binary under systemd. Download it from the Traefik releases page, and use the file provider instead of labels: you write routers and services in a YAML file. The Traefik docs cover it. If you are not running containers, Nginx or Caddy are usually simpler.

When to use Traefik​

  • Your apps run in Docker or Kubernetes, and you add or remove them often.
  • You want routing and certificates to follow container labels, with no proxy config to edit.

When not to use it:

  • You run apps directly on the server, not in containers. Traefik's main strength does not apply.
  • You need detailed caching, rewrites or static file serving. Nginx does more there. See Nginx with Docker Compose.
  • You want the shortest possible config. Caddy needs two lines per site. See Caddy with Docker Compose.

Frequently asked questions

How do I run Traefik with Docker Compose?

Run the traefik:v3.7 image with ports 80 and 443 published, the Docker socket mounted read-only, and --providers.docker=true. Then add labels to your app containers: traefik.enable=true, a router rule such as Host(`example.com`), and tls.certresolver for HTTPS. Traefik routes traffic to each labelled container.

How does Traefik get Let's Encrypt certificates?

Define a certificate resolver with an email, a storage file such as /letsencrypt/acme.json and the HTTP-01 challenge on the port 80 entrypoint. Add tls.certresolver=<name> to a router. Traefik requests the certificate on the first request and renews it before it expires. Keep acme.json in a volume so certificates survive restarts.

Why does Traefik return 404 page not found?

Traefik has no router that matches the request. The container is missing traefik.enable=true, the Host rule does not match the domain, the container is on a network Traefik cannot reach, or Traefik cannot read the Docker API. On Docker 29, Traefik v3.5 and older fail with 'client version 1.24 is too old'; update to v3.6 or newer.

How do I set the port for a container in Traefik?

Add the label traefik.http.services.<name>.loadbalancer.server.port=<port>. Traefik needs it when the image exposes no port or several ports. Do not publish the port with ports: in Compose; Traefik reaches the container over the Docker network.

Is it safe to mount the Docker socket into Traefik?

Access to the Docker socket gives control of Docker, which is root on the host, even when mounted read-only. It is the standard setup for Traefik's Docker provider. To reduce the risk, put a Docker socket proxy between Traefik and Docker that allows only read requests.

Should I use Traefik or Nginx with Docker?

Traefik if you add and remove containers often: routes and certificates come from container labels, with no reload. Nginx if you need caching, rewrites or static file serving, or a config file you control line by line.



Deploy container apps without managing Traefik​

Hostim.dev runs your Docker or Git apps with HTTPS, internal networking and logs built in. No proxy, no labels, no certificate storage.

👉 Try deploying any Docker or Git app on Hostim.dev