Schema architettura Traefik come reverse proxy integrato con Docker

Traefik as a Reverse Proxy for Docker: Complete Guide with Automatic SSL

If you manage multiple web services on a single server, sooner or later you have to solve the reverse proxy problem. After trying several solutions, I switched to Traefik as a Docker-integrated reverse proxy and it’s been one of the best infrastructure decisions I’ve made in years. In this article I share my hands-on experience, starting from the production configuration running on this very server.

What is Traefik and why choose it as a reverse proxy for Docker

Traefik is a modern reverse proxy and load balancer, built natively for containerized environments. Unlike Nginx or Apache configured manually, Traefik integrates directly with Docker and updates its configuration automatically every time a container is started or stopped.

The main advantages that convinced me:

  • Zero-touch configuration: just add labels to the Docker container, no separate config files to edit
  • Automatic SSL with Let’s Encrypt: certificates are generated and renewed without manual intervention
  • Built-in web dashboard to monitor routers, services and middleware
  • Native support for Docker, Kubernetes, Consul and other providers
  • Powerful middleware: redirects, basic/OAuth authentication, rate limiting, compression and much more

My architecture with Traefik and Docker

My server runs an infrastructure of several services, all orchestrated via Docker Compose and connected on the same dedicated internal bridge network. Traefik is the only container exposing ports to the outside world and acts as the single entry point for all traffic.

The active services are:

  • Traefik — the reverse proxy, exposed on ports 80 and 443
  • WordPress (this blog) — accessible at rogue1.it and www.rogue1.it
  • Various internal services — each on its own subdomain, not directly exposed
  • Databases — not exposed externally, only accessible via the internal network

No container other than Traefik has ports mapped on the host: all traffic must go through the reverse proxy. This enormously simplifies security management.

Configuring Traefik with Docker Compose

Here is the docker-compose.yml structure for Traefik, with the most important points commented. Domains in the code blocks are replaced with example.com for illustration purposes.

Entrypoints: the listening ports

Entrypoints define on which ports Traefik listens. A typical configuration includes at least HTTP and HTTPS:

command:
  - "--entrypoints.web.address=:80"
  - "--entrypoints.websecure.address=:443"

Traefik also supports generic TCP entrypoints, useful for handling other protocols (mail, federation, etc.) while maintaining a single entry point for all server traffic.

Docker provider: the auto-discovery magic

The --providers.docker=true parameter enables the Docker provider: Traefik connects to the Docker socket in read-only mode and monitors running containers in real time. When a container is started with the label traefik.enable=true, Traefik automatically registers it as a service.

volumes:
  - /var/run/docker.sock:/var/run/docker.sock:ro

Mounting the socket in read-only mode (:ro) is a security best practice: Traefik reads container state but cannot modify them.

Automatic SSL with Let’s Encrypt

This is probably Traefik’s most appreciated feature: automatic SSL certificate renewal via Let’s Encrypt. The configuration is minimal:

- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=you@email.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"

I use the HTTP challenge (http-01): Let’s Encrypt verifies domain ownership by making an HTTP request on port 80. Certificates are saved in the acme.json file on a persistent volume and renewed automatically before expiry.

Routing with Docker Labels

The heart of every service configuration lies in Docker labels. Instead of a centralised configuration file, each container declares its own routing rules. Here is a complete example:

labels:
  - "traefik.enable=true"
  # HTTP Router: redirect to HTTPS
  - "traefik.http.routers.myapp-http.rule=Host(`example.com`) || Host(`www.example.com`)"
  - "traefik.http.routers.myapp-http.entrypoints=web"
  - "traefik.http.routers.myapp-http.middlewares=myapp-https-redirect"
  - "traefik.http.middlewares.myapp-https-redirect.redirectscheme.scheme=https"
  - "traefik.http.middlewares.myapp-https-redirect.redirectscheme.permanent=true"
  # HTTPS Router: actual traffic
  - "traefik.http.routers.myapp.rule=Host(`example.com`) || Host(`www.example.com`)"
  - "traefik.http.routers.myapp.entrypoints=websecure"
  - "traefik.http.routers.myapp.tls=true"
  - "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
  - "traefik.http.services.myapp.loadbalancer.server.port=80"

The HTTP → HTTPS redirect pattern

A pattern I repeat for every service is the double router: one for HTTP (port 80) that permanently redirects to HTTPS, and one for HTTPS (port 443) that handles actual traffic with TLS. The redirectscheme middleware handles the 301 redirect, which is also correct for SEO.

The Traefik Dashboard

Traefik includes a very useful web dashboard for monitoring. Best practice is to expose it on a dedicated subdomain accessible only via HTTPS, with mandatory basic authentication:

- "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=auth"
- "traefik.http.middlewares.auth.basicauth.users=YOURUSER:BCRYPT_HASH"

The special service api@internal is the dashboard itself. The basicauth middleware protects access with a bcrypt-hashed password — never in plain text. Generate the bcrypt hash with htpasswd -nB username. Important: in docker-compose, $ symbols must be escaped as $$.

From the dashboard you can see in real time all active routers, services, applied middleware and TLS certificate status. It’s an indispensable debugging tool, but always keep it protected: it exposes the entire infrastructure map.

Adding a new service: practical example

To show how simple it is to add a new service, here is the generic configuration for an application on its own dedicated subdomain:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.myservice-http.rule=Host(`myservice.example.com`)"
  - "traefik.http.routers.myservice-http.entrypoints=web"
  - "traefik.http.routers.myservice-http.middlewares=myservice-https-redirect"
  - "traefik.http.middlewares.myservice-https-redirect.redirectscheme.scheme=https"
  - "traefik.http.middlewares.myservice-https-redirect.redirectscheme.permanent=true"
  - "traefik.http.routers.myservice.rule=Host(`myservice.example.com`)"
  - "traefik.http.routers.myservice.entrypoints=websecure"
  - "traefik.http.routers.myservice.tls=true"
  - "traefik.http.routers.myservice.tls.certresolver=letsencrypt"
  - "traefik.http.services.myservice.loadbalancer.server.port=8080"

Simply replace myservice with your service name, update the subdomain, and indicate the internal port the container listens on. No ports exposed on the host, no changes to centralised config files: Traefik reads the labels and configures the routing on its own within seconds.

Security considerations

Security practices I apply in my configuration:

  • Docker socket in read-only: /var/run/docker.sock:/var/run/docker.sock:ro — Traefik reads but does not write
  • Dashboard protected by basic auth with bcrypt-hashed password, never plain text
  • API dashboard not insecure: --api.insecure=false, exposed only via authenticated router on HTTPS
  • No service ports exposed on the host: only Traefik has mapped ports, other containers communicate only on the internal network
  • HTTPS enforced on all services with permanent 301 redirect
  • Dashboard subdomain not obvious: avoid predictable names like admin. or traefik. to reduce bot attack surface

Pros and cons after months in production

Pros

  • Adding a new service requires only labels in docker-compose, no other configuration
  • SSL certificates have never expired, renewal is completely automatic
  • The dashboard makes the state of the entire infrastructure immediately visible
  • Traefik logs are very detailed and simplify debugging
  • Active community and excellent documentation

Cons

  • Label syntax can become verbose for complex configurations
  • Middleware debugging requires some initial practice
  • Escaping $ in docker-compose labels (which become $$) is a common source of errors
  • Highly customised configurations require additional static configuration files

Conclusions

Using Traefik as a Docker-integrated reverse proxy has enormously simplified the management of my infrastructure. The configuration-as-code approach via Docker labels, automatic SSL management, and the monitoring dashboard make it the ideal choice for anyone managing multiple containerised services on a single server.

If you’re still using Nginx or Apache with manual configurations in a Docker environment, I recommend evaluating a migration to Traefik: the time invested in the initial learning curve pays off quickly through the simplicity of day-to-day management.

Have questions about the configuration or want to share your Traefik experience? Leave a comment below!