KAHIBARO
Discord Login Register

22.6. Traefik

Why Traefik?

Traefik is a modern HTTP reverse proxy and load balancer, designed specifically with containers and dynamic infrastructure in mind. While Nginx and similar servers work very well, they were originally built for static configuration files. Traefik focuses on:

For a backend developer who uses Docker or container orchestration, Traefik often requires much less manual configuration than traditional proxies.

Traefik’s Core Concepts

To understand Traefik, you need a set of basic concepts. They are similar to what you saw in the Reverse Proxies and Nginx chapters, but Traefik has its own terminology.

EntryPoints

An EntryPoint defines where Traefik listens for incoming traffic. For example:

A minimal Traefik configuration might define two EntryPoints: web and websecure.

Example static configuration (YAML style):

yaml
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

You can think of an EntryPoint as: “Which port does Traefik open and what protocol does it use?”

Routers

A Router connects an EntryPoint to a specific backend service, based on rules that match each incoming request.

Routers can match on:

Router responsibilities:

Example:

yaml
http:
  routers:
    api-router:
      rule: "Host(`api.example.com`) && PathPrefix(`/`)"
      entryPoints:
        - websecure
      service: api-service
      tls: {}

Here, any HTTPS request on websecure whose Host is api.example.com is sent to api-service.

Services

A Service in Traefik represents one or more backend servers, for example your FastAPI app running in a Docker container or multiple replicas behind load balancing.

A simple service might be defined as:

yaml
http:
  services:
    api-service:
      loadBalancer:
        servers:
          - url: "http://app1:8000"
          - url: "http://app2:8000"

Traefik will load balance requests between app1 and app2.

Middlewares

Middlewares are filters or modifiers applied between the client and the service. Typical uses:

Example middleware that redirects HTTP to HTTPS:

yaml
http:
  middlewares:
    redirect-to-https:
      redirectScheme:
        scheme: https
        permanent: true

Then you attach it to a router:

yaml
http:
  routers:
    http-router:
      rule: "Host(`api.example.com`)"
      entryPoints:
        - web
      middlewares:
        - redirect-to-https
      service: api-service

Providers

Providers are where Traefik discovers configuration from. Common providers include:

When you use Docker, Traefik reads labels on your containers and automatically creates routers, services, and middlewares.

Static vs Dynamic Configuration

Traefik splits configuration into:

Static configuration is often set through a traefik.yml or command line flags, and dynamic configuration is often provided by Docker labels or additional config files that Traefik watches.

Important rule: Static configuration requires a restart of Traefik to change, dynamic configuration does not. Use dynamic configuration for things that depend on running applications, like routes and backends.

Running Traefik with Docker

For backend projects, Traefik shines when combined with Docker and Docker Compose. Instead of editing configuration files for every new service, you add a set of labels to containers.

Basic `docker-compose.yml` with Traefik

Here is a minimal example that exposes a FastAPI container behind Traefik, without HTTPS yet:

yaml
version: "3.8"
services:
  traefik:
    image: traefik:v3.0
    command:
      - "--api.dashboard=true"
      - "--api.insecure=true"
      - "--entrypoints.web.address=:80"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
    ports:
      - "80:80"
      - "8080:8080"   # Dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
  api:
    image: my-fastapi-image
    container_name: api
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.localhost`)"
      - "traefik.http.routers.api.entrypoints=web"
      - "traefik.http.services.api.loadbalancer.server.port=8000"

Explanation of the important parts:

With this setup:

Mapping Hostnames for Local Development

In local development, you typically do not own a domain. You can still use host-based routing by editing your hosts file.

For example, add:

text
127.0.0.1  api.localhost
127.0.0.1  web.localhost

Then you can create different routers for different services:

yaml
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.api.rule=Host(`api.localhost`)"
  - "traefik.http.routers.web.rule=Host(`web.localhost`)"

This lets you simulate multiple subdomains on your laptop.

Using Traefik for HTTPS and Let's Encrypt

One of Traefik’s most popular features is automatic certificate management with Let's Encrypt.

Basic HTTPS Configuration with ACME

Here is an example static configuration snippet for Traefik using Let's Encrypt:

yaml
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
certificatesResolvers:
  letsencrypt:
    acme:
      email: "you@example.com"
      storage: "/letsencrypt/acme.json"
      httpChallenge:
        entryPoint: web

This instructs Traefik to:

In Docker Compose:

yaml
services:
  traefik:
    image: traefik:v3.0
    command:
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt

Now your services can reference the letsencrypt resolver to get HTTPS certificates.

Creating an HTTPS Router for Your API

Your Dockerized FastAPI service might look like this:

yaml
services:
  api:
    image: my-fastapi-image
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.example.com`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"
      - "traefik.http.services.api.loadbalancer.server.port=8000"

Traefik will:

Redirecting HTTP to HTTPS

You usually want all HTTP traffic on port 80 redirected to HTTPS.

You can apply a middleware through labels:

yaml
services:
  traefik:
    # same as above
  api:
    image: my-fastapi-image
    labels:
      - "traefik.enable=true"
      # HTTPS router
      - "traefik.http.routers.api-https.rule=Host(`api.example.com`)"
      - "traefik.http.routers.api-https.entrypoints=websecure"
      - "traefik.http.routers.api-https.tls.certresolver=letsencrypt"
      - "traefik.http.routers.api-https.service=api"
      # HTTP -> HTTPS router
      - "traefik.http.routers.api-http.rule=Host(`api.example.com`)"
      - "traefik.http.routers.api-http.entrypoints=web"
      - "traefik.http.routers.api-http.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.services.api.loadbalancer.server.port=8000"

Common Middleware Examples

Middlewares in Traefik can solve many everyday problems in backend APIs.

1. Stripping a Path Prefix

Imagine your application expects / but you want to expose it at /api. Instead of changing your app, you can use stripPrefix.

Example:

yaml
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.api.rule=Host(`api.example.com`) && PathPrefix(`/api`)"
  - "traefik.http.routers.api.entrypoints=websecure"
  - "traefik.http.routers.api.tls.certresolver=letsencrypt"
  - "traefik.http.routers.api.middlewares=api-strip"
  - "traefik.http.middlewares.api-strip.stripprefix.prefixes=/api"
  - "traefik.http.services.api.loadbalancer.server.port=8000"

Client requests:

Traefik forwards:

2. Adding Security Headers

You can add common security headers without changing your application code.

yaml
labels:
  - "traefik.http.middlewares.sec-headers.headers.stsSeconds=31536000"
  - "traefik.http.middlewares.sec-headers.headers.stsIncludeSubdomains=true"
  - "traefik.http.middlewares.sec-headers.headers.contentTypeNosniff=true"
  - "traefik.http.middlewares.sec-headers.headers.browserXssFilter=true"
  - "traefik.http.routers.api.middlewares=sec-headers"

These labels configure a sec-headers middleware which is attached to the api router.

Important security tip: Do not rely only on Traefik for security. Add secure headers, rate limiting, and authentication at the proxy level, but also validate inputs and protect your application itself.

3. Basic Authentication for Internal Tools

You might want a simple password prompt for an internal dashboard.

yaml
labels:
  - "traefik.http.middlewares.simple-auth.basicauth.users=user:$$apr1$$0AD..../$F5X..."
  - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
  - "traefik.http.routers.dashboard.middlewares=simple-auth"
  - "traefik.http.routers.dashboard.service=api@internal"

Here, api@internal is Traefik’s own dashboard service. The password is a hashed value generated by tools like htpasswd.

Load Balancing with Traefik

Traefik supports multiple load balancing algorithms, such as:

Typical use case, multiple replicas of an API:

yaml
services:
  api1:
    image: my-fastapi-image
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.example.com`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"
      - "traefik.http.services.api.loadbalancer.server.port=8000"
  api2:
    image: my-fastapi-image
    labels:
      - "traefik.enable=true"
      # Share the same service name "api"
      - "traefik.http.services.api.loadbalancer.server.port=8000"

Traefik groups containers with the same service configuration and balances traffic between them.

You can also set weights:

yaml
labels:
  - "traefik.http.services.api.loadbalancer.server.port=8000"
  - "traefik.http.services.api.loadbalancer.sticky.cookie=true"
  - "traefik.http.services.api.loadbalancer.serversTransport=mytransport"

Or in file-based configuration, you can specify weights per server.

Monitoring and Debugging Traefik

Traefik provides:

Enabling the Dashboard

You can enable the (insecure) dashboard in development via command options:

yaml
command:
  - "--api.dashboard=true"
  - "--api.insecure=true"

Then open http://localhost:8080 to see:

This is extremely useful when learning or debugging, but you must avoid --api.insecure=true in production.

Access Logs

Access logs help you see what requests Traefik handles:

yaml
command:
  - "--accesslog=true"
  - "--accesslog.format=json"

You can pipe these logs to a central logging system or inspect them directly.

When to Choose Traefik vs Nginx

Both Traefik and Nginx can act as reverse proxies for your backend.

A short comparison:

FeatureTraefikNginx
Dynamic discoveryNative (Docker, Kubernetes, etc.)Manual or using extra tooling
Configuration styleLabels, TOML/YAMLStatic config files
Automatic HTTPS (Let's Encrypt)Built-in ACME supportVia additional tools or custom config
DashboardBuilt-in, very visualThird-party or manual
Best suited forContainer-based, dynamic environmentsStatic or traditional server setups

As an absolute beginner:

You do not have to commit to one forever. You can start with whichever seems easier, then learn the other later.

Example: FastAPI Behind Traefik in Production

To tie everything together, here is a simplified production-style Compose configuration for a FastAPI backend with PostgreSQL, using Traefik for HTTPS.

yaml
version: "3.8"
services:
  traefik:
    image: traefik:v3.0
    command:
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--accesslog=true"
      - "--api.dashboard=true"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # remove or protect in real production
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
  api:
    image: my-fastapi-image
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/app
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.example.com`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"
      - "traefik.http.routers.api.middlewares=sec-headers@docker"
      - "traefik.http.services.api.loadbalancer.server.port=8000"
      - "traefik.http.middlewares.sec-headers.headers.contentTypeNosniff=true"
      - "traefik.http.middlewares.sec-headers.headers.stsSeconds=31536000"
      - "traefik.http.middlewares.sec-headers.headers.stsIncludeSubdomains=true"
  db:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=app
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:

In this example:

This pattern is common in real-world backend systems. Traefik becomes the single entry point for all HTTP(S) traffic, and your backend services remain simple, focused, and unaware of TLS, host-based routing, or redirects.

By understanding these Traefik basics, you can design and deploy container-based backend systems that are easier to manage, more secure, and highly flexible as you add more services over time.

Views: 8

Comments

Please login to add a comment.

Don't have an account? Register now!