Traefik reverse proxy for Docker

March 21st 2025 Traefik Docker

When I needed a reverse proxy to publicly expose a couple of web applications running in Docker, I decided to use Traefik after doing a short research. Now that I successfully configured it, I don't regret the choice. Even without any previous experience, the whole process was much simpler than I expected.

This post is a short getting-started guide for configuring the Traefik itself and for exposing a new Docker container through it. It's something I failed to find while setting up my instance. I plan to use it myself the next time I'll have to expose a new service. Feel free to do the same, but keep in mind that I'm still pretty much a Traefik beginner and the described steps aren't necessarily the recommended best practice.

Of course, I decided to host Traefik in Docker, too, using the official Docker image and a fixed version, as recommended. I used Docker compose to configure it:

image: traefik:v3.3

I set its configuration settings through command line arguments:

  • I enabled insecure API to make the dashboard work without authentication on insecure connection:
    command:
      - --api.insecure=true
    
  • I chose the Docker provider for routing configuration. This allowed me to configure routing for Docker containers using labels. I wanted to explicitly select the containers to be exposed:
    command:
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
    
  • I configured both HTTP and HTTPS endpoints, and established automatic redirect from HTTP to HTTPS:
    command:
      - --entryPoints.http.address=:80
      - --entryPoints.https.address=:443
      - --entryPoints.http.http.redirections.entryPoint.to=https
      - --entryPoints.http.http.redirections.entryPoint.scheme=https
    
  • I configured automatic Let's Encrypt certificate generation with TLS challenge. I put my email in the .env so that I could safely commit the docker-compose.yml without it. And explicitly set the storage file so that I could map it to a volume.

    command:
      - --certificatesResolvers.letsEncrypt.acme.storage=/etc/traefik/acme/acme.json
      - --certificatesResolvers.letsEncrypt.acme.email=${LETSENCRYPT_EMAIL}
      - --certificatesResolvers.letsEncrypt.acme.tlsChallenge=true
    

Based on the configuration so far, I had to map and make publicly available the HTTP and HTTPS ports, and also mapped the 8080 port with the dashboard for my internal use only.

ports:
  - "80:80"
  - "443:443"
  - "8080:8080"

In addition to the already mentioned Let's encrypt storage file, Traefik in Docker mode also requires access to the Docker API, so I mapped the Docker socket:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock
  - ./volumes/traefik/letsencrypt:/etc/traefik/acme

With all that in place, the Traefik container was configured. From here on, I can expose individual containers over HTTPS using labels only:

  • I need to explicitly enable Traefik routing first. This will expose the lowest mapped port of the container:
    labels:
      - traefik.enable=true
    
  • All router settings include a unique router name, myservice in this example:
    • I expose the service on a unique host name (read from the .env file). Of course, it needs to be entered in the DNS server, as well:
      labels:
        - traefik.http.routers.myservice.rule=Host(`${MYSERVICE_DOMAIN}`)
      
    • I make it available via HTTPS using the Let's Encrypt certificate resolver configured in Traefik:
      labels:
        - traefik.http.routers.myservice.tls=true
        - traefik.http.routers.myservice.tls.certResolver=letsEncrypt
      
    • I assign a set of middlewares by their unique name. Their configuration follows below:
      labels:
        - traefik.http.routers.myservice.middlewares=myservice-compress,myservice-headers
      
  • Similar to the router settings, each middleware has its settings grouped by its own unique name, for example:
    • Compress middleware being enabled:
      labels:
        - traefik.http.middlewares.myservice-compress.compress=true
      
    • Headers middleware with some headers set:
      labels:
        - traefik.http.middlewares.myservice-headers.headers.forceSTSHeader=true
        - traefik.http.middlewares.myservice-headers.headers.referrerPolicy=no-referrer-when-downgrade
      

That's enough to make the service publicly exposed. Once its container starts, Traefik will automatically obtain a Let's Encrypt certificate for its domain and expose it over HTTPS. And it will take care of certificate renewal as well. I don't think it can get much simpler than this.

Traefik was a really pleasant surprise. It had a somewhat steep learning curve at the beginning. But once I grasped the basics, I was able to efficiently find my way around its documentation, and configure the services to my liking. It's been running without any issues for a while now, and I hope it continues to for much longer.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License