Installing and Configuring Traefik as Your Reverse Proxy

Installing and Configuring Traefik as Your Reverse Proxy

We earn commissions when you shop through the links on this page, at no additional cost to you. Learn more.

Traefik transformed how I manage multiple services on my homelab. Unlike Nginx, which requires manual configuration for each service, Traefik automatically discovers Docker containers and routes traffic to them—no restart needed. I'm going to walk you through installing Traefik, configuring it with Let's Encrypt SSL, and getting it running with real Docker services in under an hour.

Why Traefik Over Nginx or Caddy?

I've used all three, and here's my honest take: Traefik shines when you run many services in Docker and want zero downtime updates. When I deploy a new container with the right labels, Traefik picks it up instantly. Nginx requires manual configuration edits and reloads. Caddy is simpler but less flexible for complex routing rules.

The trade-off is complexity. Traefik's configuration file is longer, and its concepts (entrypoints, routers, services, middlewares) take time to grok. But once you understand the mental model, you'll never go back.

If you're running this on a budget VPS—say a $40/year option from RackNerd with 2GB RAM—Traefik uses roughly 80–120 MB, leaving plenty for your actual services. That's the sweet spot I've found for small self-hosted setups.

Prerequisites and What You Need

You'll need:

I'm running this on a Debian 12 machine, but the Docker Compose approach works on Ubuntu, CentOS, or any modern Linux distro.

Setting Up the Directory Structure

First, create a dedicated folder for Traefik and its config:

mkdir -p /opt/traefik/config
cd /opt/traefik
mkdir -p data
touch data/traefik.yml
touch data/acme.json
chmod 600 data/acme.json

The acme.json file stores your SSL certificates. It must have restrictive permissions (600) or Let's Encrypt will refuse to write to it.

Creating the Traefik Configuration File

I prefer defining Traefik's core settings in a YAML file rather than command-line flags. Create data/traefik.yml:

api:
  insecure: false
  dashboard: true
  debug: false

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          regex: "^http://(.*)$"
          replacement: "https://$1"
          permanent: true
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: traefik-network
  file:
    filename: /etc/traefik/config.yml
    watch: true

certificatesResolvers:
  letsencrypt:
    acme:
      email: "[email protected]"
      storage: /etc/traefik/acme.json
      httpChallenge:
        entryPoint: web

log:
  level: INFO
  filePath: /var/log/traefik.log

accessLog:
  filePath: /var/log/traefik-access.log

Replace [email protected] with a real email address. This is where Let's Encrypt sends renewal reminders. The exposedByDefault: false setting means only containers I explicitly label will be exposed—better security.

The Docker Compose Setup

Now create docker-compose.yml in your /opt/traefik directory:

version: '3.8'

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - traefik-network
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./data/acme.json:/etc/traefik/acme.json
      - ./data/config.yml:/etc/traefik/config.yml:ro
      - /var/log/traefik:/var/log/traefik
    environment:
      - TZ=UTC
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(\`traefik.example.com\`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.middlewares=auth@file"

  whoami:
    image: traefik/whoami
    container_name: whoami
    restart: unless-stopped
    networks:
      - traefik-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(\`whoami.example.com\`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
      - "traefik.http.services.whoami.loadbalancer.server.port=80"

networks:
  traefik-network:
    driver: bridge

volumes:
  acme-data:
Watch out: Replace traefik.example.com and whoami.example.com with your actual domain names. Traefik uses these hostnames to route traffic, so DNS must resolve them to your server's IP.

I've included a test service, whoami, which echoes back your request headers. It's perfect for verifying Traefik is routing correctly.

Creating a Middleware Config File

For the dashboard to be secure, create data/config.yml with basic auth:

http:
  middlewares:
    auth:
      basicAuth:
        users:
          - "admin:$apr1$sR8n/w/8$bCMZZn3y7mM7sL5s0f9Zz0"

tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

The hash above is for username admin and password password. To generate your own, use:

htpasswd -nb admin yourpassword | openssl enc -base64

Launching Traefik

From your /opt/traefik directory, start it up:

docker-compose up -d
docker-compose logs -f traefik

Watch the logs for a few seconds. You should see Traefik detecting the whoami container and requesting SSL certificates from Let's Encrypt. This typically takes 30–60 seconds.

Connecting Existing Services

To add an existing service to Traefik, add these labels to your Docker container:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.myapp.rule=Host(\`myapp.example.com\`)"
  - "traefik.http.routers.myapp.entrypoints=websecure"
  - "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
  - "traefik.http.services.myapp.loadbalancer.server.port=8080"

Replace myapp with your service name and 8080 with the port it listens on inside the container. Traefik will auto-detect it and route traffic.

Tip: Keep your router names, service names, and domain hostnames consistent. I use a pattern like myapp.example.com where the router is myapp. This makes debugging much easier when you have 10+ services.

Monitoring and Troubleshooting

Check container status:

docker-compose ps

View real-time logs:

docker-compose logs -f traefik

Common issues I've hit:

Performance and Resource Usage

On a 2GB VPS, Traefik with 15+ services uses roughly 100–150 MB RAM and nearly zero CPU at idle. Under moderate load (a few requests per second), CPU bumps to 5–10%. It's very efficient compared to running separate Nginx instances for each service.

If you're on a truly budget setup—say a $40/year RackNerd entry-level VPS—this will work fine. You'll have room for one or two additional Docker services without breaking a sweat.

Next Steps

Once you're comfortable with basic routing, explore Traefik's middleware options: rate limiting, request rewriting, compression, and custom headers. I use middleware to add security headers to all my services in one place, which beats configuring each app individually.

Also consider running watchtower alongside Traefik to auto-update your containers. With Traefik's dynamic discovery, you can redeploy services without touching the reverse proxy config—pure infrastructure bliss.

Discussion