Using Traefik as reverse proxy - convention over configuration

Insider Blog

Traefik is a reverse proxy with excellent docker integration. It uses labeln attached to containers to route traffic to them.

A common label set looks similar to this:

whoami/docker-compose.yaml
services:
  whoami:
    image: traefik/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls=true"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"

In this example:

  • whoami the name of the "main" service (2)
  • whoami the also the default compose project name
  • Traefik is activated (5)
  • It is exposed under whoami subdomain (6)
  • It is served over https (7)
  • A preconfigured certresolver named letsencrypt is used for tls (8-9)

The problem

In my other post about gitops with docker I introduced a concept of using git as the sources of truth for docker deployments using docker compose.

Now there are some requirements to this approach:

  • every stack is exposer under <stack_name>.<your_domain>
  • every stack is protected with tls

We could of course edit every docker-compose.yaml file and add the required labels, but it quickly becomes obvious, that all the labels are the same!

  1. Enable Traefik
  2. Assign a subdomain
  3. Enable HTTPS

The solution

By using $COMPOSE_PROJECT_NAME variable we can create a more generic template. This ensures that the Traefik rules are consistent.

<stack_name>/docker-compose.yaml
services:
  <main_service>:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.$COMPOSE_PROJECT_NAME.rule=Host(`<stack_name>.example.com`)"
      - "traefik.http.routers.$COMPOSE_PROJECT_NAME.entrypoints=websecure"
      - "traefik.http.routers.$COMPOSE_PROJECT_NAME.tls=true"
      - "traefik.http.routers.$COMPOSE_PROJECT_NAME.tls.certresolver=letsencrypt"

The good news is that Traefik lets us configure some defaults that will cover the above boilerplate!

Let's us define two simple conventions:

  1. app is the main container, where Traefik will route the traffic
  2. <stack_name> (the compose project name) is the subdomain
whoami/docker-compose.yaml
services:
  app:
    image: traefik/whoami

Now let's configure traefik to do "the magic"

traefik/docker-compose.yaml
volumes:
  letsencrypt:
    
services:
  traefik:
    container_name: traefik
    restart: always
    image: traefik:3
    network_mode: host
    command:
      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
      - --certificatesresolvers.letsencrypt.acme.email=<your_email_here>
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web

      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.http.tls.certresolver=letsencrypt

      - --providers.docker
      - --providers.docker.defaultrule=Host(`{{ trimPrefix `app-` .Name }}.example.com`)
      - --providers.docker.constraints=Label(`com.docker.compose.service`,`app`)
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /letsencrypt:/letsencrypt

Let's explain this a little

  1. We configure a Let's Encrypt certificates resolvers (11-14)
  2. We listen on port 80 and 443 (16-17)
  3. We redirect all http traffic to https (19-20)
  4. and attach the configured certificates resolvers to it (21)
  5. We setup the docker provider (23-25)

Explanation for lines 24-25

--providers.docker.defaultrule=Host(`{{ trimPrefix 'app-'.Name }}.example.com`)
  • .Name is autogenerated as <service-name>-<stack_name> so for your whoami example it would be app-whoami
  • trimPrefix 'app-'.Name resolves in whoami

--providers.docker.constraints=Label(`com.docker.compose.service`,`app`)

All services should be exposed by default but should be filtered down, only to app services. The label com.docker.compose.service is added by docker-compose to all containers .

Bonus configuration

We can tweak this configuration even more.

Exposing other services in the stack

Sometimes you want to expose more than just the app container.

The current configuration won't route traffic to any other services other than app. To still be able to use the default configuration method, we need to re-enable it.

--providers.docker.constraints=Label(`com.docker.compose.service`,`app`) || Label(`traefik.enable`, `true`)

Stack name other than directory name

When deploying the stacks, the name is generated based on directory name where the docker-compose.yaml file is located. We can change the make in several ways, but here is the easiest one:

whoami-example/docker-compose.yaml
name: whoami
services:
  app:
    image: traefik/whoami

Author

Robert Juzak , B.Sc.

Characteristics

released:

March 13, 2026

categories:

What moves us, DevOps

Tags:

DevOps
previous article