This is the docker-compose file for the main Traefik container. For now, the only services routed through Traefik are using http. See the end of this page for https info.
services:
traefik:
image: "traefik:latest"
ports:
- "80:80"
- "8080:8080"
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedByDefault=false"
- "--entrypoints.web.address=:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
Port 80 is the port listening for incoming (http) traffic. Port 8080 is for the Traefik dashboard, which shows all the configured services in a nice-looking web UI.
Commands:
- api.insecure = allows access to the API (https://community.traefik.io/t/what-api-insecure-do-exactly/1712/4). Is this even needed?
- providers.docker = allows integration with Docker
- providers.docker.exposedByDefault = Determines whether Docker containers are automatically set up for access. Since this is false, I have to add labels to the specific containers I want to use with Traefik (if true, all running containers would be exposed).
- entrypoints.web.address = creates an entrypoint (Traefik terminology) called
web.
The volumes line mounts the Docker socket as read-only, allowing Traefik dynamically configure services (https://doc.traefik.io/traefik/providers/docker/). This is technically not secure; see the link for more information.
Once Traefik is deployed with the above configuration, add the Traefik labels to the docker-compose files for your other services. These labels tell Traefik to expose the container and which hostname/port to use.
Here is a simple example (with some unrelated lines removed):
services:
flame:
image: flame:multiarch
[...]
labels:
- "traefik.enable=true"
- "traefik.http.routers.flame.rule=Host(`go`)"
networks:
- traefik_default
networks:
traefik_default:
external: true
The labels are the following:
- traefik.enable = self-explanatory
- traefik.http.routers.flame.rule=Host(`go`) = creates a router (Traefik terminology) called
flamewhich maps to the DNS hostname go. The router name must be unique for every service. DNS must be set up separately by you, as Traefik has nothing to do with it.
Regarding the network configuration: When a docker-compose stack is deployed, a separate network is created for that stack (https://docs.docker.com/compose/networking/). Since I deployed Traefik with docker-compose, a network called traefik_default is created. Any containers/applications that I want to expose through Traefik must be on that network. My applications are deployed with Docker Compose, so I’ll need to update their docker-compose files.
There are two parts to the network setup:
- At the top-level network tier (which I have at the bottom of the file), define the existing
traefik_defaultnetwork. External indicates that the stack will join an existing network rather than creating a new one. - Second, within the services key, specify that the container should be joined to
traefik_default.
Once you save and re-deploy the stack, the service should appear in your Traefik dashboard and be accessible at the DNS name you specified.
This works fine for single-container stacks, but for anything more complicated (stacks with multiple containers, like a web frontend + a database), I had to connect the Traefik container to the other stack’s network. Maybe there’s a better way, but I couldn’t find one.
services:
traefik:
image: "traefik:latest"
[..]
networks:
- bookstack_default
- librenms_default
- traefik
volumes:
certs:
networks:
bookstack_default:
external: true
librenms_default:
external: true
traefik:
name: traefik_default
Other things to keep in mind:
- Port mappings for your containers can be removed since you’re now accessing them through Traefik.
- If you have a stack with multiple containers, only the web/frontend needs to be labeled for use with Traefik.
Let’s Encrypt/certificate setup (https://major.io/2021/08/16/wildcard-letsencrypt-certificates-traefik-cloudflare/)
Here is the final Traefik docker-compose configuration with SSL/TLS support. I obtain a Let’s Encrypt wildcard certificate using Cloudflare to do DNS authentication since the server doesn’t have any ports open to the internet.
Important additions:
- Port 443 mapping
- Command section:
- entry point for port 443
- configure entry point for TLS, Let’s Encrypt, and specify the cert’s domain name + wildcard
- specify Let’s Encrypt DNS challenge & parameters
- Environment variables: Cloudflare user account + API key
- Add a volume called certs to store the certificates & attach it to the container
services:
traefik:
image: "traefik:latest"
ports:
- "80:80"
- "8080:8080"
- "443:443"
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedByDefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
# Set up the TLS configuration for our websecure listener
- "--entrypoints.websecure.http.tls=true"
- "--entrypoints.websecure.http.tls.certResolver=letsencrypt"
- "--entrypoints.websecure.http.tls.domains[0].main=my.domain.com"
- "--entrypoints.websecure.http.tls.domains[0].sans=*.my.domain.com"
# Set up LetsEncrypt
- "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
- "--certificatesresolvers.letsencrypt.acme.email=jmyemail@domain.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
environment:
- CLOUDFLARE_EMAIL=mycloudflareusername@domain.com
- CLOUDFLARE_DNS_API_TOKEN=my_cloudflare_api_token
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- certs:/letsencrypt
networks:
- bookstack_default
- librenms_default
- traefik
volumes:
certs:
networks:
bookstack_default:
external: true
librenms_default:
external: true
traefik:
name: traefik_default
Then, for each container to be accessed over TLS, update its stack to include the secure entry point and certificate resolver defined in the config above (in this case, it’s called letsencrypt):
labels:
- "traefik.enable=true"
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime.cloud.zaxz.pw`)"
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
- "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
With the above config, accessing the URL over http will result in an error. To fix this, set up an http listener and redirect it to https:
labels:
- "traefik.enable=true"
- "traefik.http.routers.uptime-kuma-http.rule=Host(`uptime.cloud.zaxz.pw`)"
- "traefik.http.routers.uptime-kuma-http.entrypoints=web"
- "traefik.http.routers.uptime-kuma-http.middlewares=uptime-kuma"
- "traefik.http.middlewares.uptime-kuma.redirectscheme.scheme=https"
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime.cloud.zaxz.pw`)"
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
- "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"