Skip to main content

Overview

Traefik v3 serves as the edge router and API gateway for the microservices-app. It provides:
  • HTTP/2 and gRPC routing
  • Service discovery (Docker labels and Kubernetes CRDs)
  • Middleware (CORS, rate limiting, retries, authentication)
  • OpenTelemetry tracing integration

Architecture

Traefik sits at the edge and routes traffic to backend services:
Client → Traefik (port 30081)
           ├─→ /greeter.v1.GreeterService → greeter-service:8080 (h2c)
           ├─→ /gateway.v1.GatewayService → gateway:8082 (h2c)
           ├─→ /auth → auth-service:8090 (HTTP)
           └─→ / → frontend:80 (HTTP)

Docker Compose Configuration

For local development with Docker Compose, Traefik uses labels for service discovery.

Traefik Service

From docker-compose.yml:
traefik:
  image: traefik:v3.6
  ports:
    - "30081:80"
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
    - ./deploy/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
    - ./deploy/traefik/dynamic:/etc/traefik/dynamic:ro
  networks:
    - app

Static Configuration

From deploy/traefik/traefik.yml:
entryPoints:
  web:
    address: ":80"

ping:
  entryPoint: web

providers:
  docker:
    exposedByDefault: false
    network: microservice-app_app
  file:
    directory: /etc/traefik/dynamic
    watch: true

log:
  level: INFO

accessLog: {}
Key settings:
  • exposedByDefault: false: Only services with traefik.enable=true are exposed
  • network: Ensures Traefik uses the correct Docker network
  • file provider: Loads middleware from deploy/traefik/dynamic/

Routing Rules

Services declare routing rules via Docker labels.

gRPC Service (Greeter)

greeter:
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.greeter.rule=PathPrefix(`/greeter.v1.GreeterService`)"
    - "traefik.http.routers.greeter.entrypoints=web"
    - "traefik.http.routers.greeter.priority=100"
    - "traefik.http.routers.greeter.middlewares=cors@file,auth@file,rate-limit@file,retry@file"
    - "traefik.http.services.greeter.loadbalancer.server.port=8080"
    - "traefik.http.services.greeter.loadbalancer.server.scheme=h2c"
Explanation:
  • Rule: Matches gRPC service path /greeter.v1.GreeterService/*
  • Priority: 100 (higher priority than frontend catch-all)
  • Middlewares: CORS, auth, rate limiting, retry (defined in middleware.yml)
  • Scheme: h2c (HTTP/2 Cleartext for gRPC)

Gateway Service

gateway:
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.gateway.rule=PathPrefix(`/gateway.v1.GatewayService`)"
    - "traefik.http.routers.gateway.entrypoints=web"
    - "traefik.http.routers.gateway.priority=100"
    - "traefik.http.routers.gateway.middlewares=cors@file,auth@file,rate-limit@file"
    - "traefik.http.services.gateway.loadbalancer.server.port=8082"
    - "traefik.http.services.gateway.loadbalancer.server.scheme=h2c"

Auth Service

auth-service:
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.auth.rule=PathPrefix(`/auth`)"
    - "traefik.http.routers.auth.entrypoints=web"
    - "traefik.http.routers.auth.priority=90"
    - "traefik.http.routers.auth.middlewares=cors@file,rate-limit@file"
    - "traefik.http.services.auth.loadbalancer.server.port=8090"
Note: Auth service does not use the auth middleware (to avoid circular dependency).

Frontend

frontend:
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.frontend.rule=PathPrefix(`/`)"
    - "traefik.http.routers.frontend.entrypoints=web"
    - "traefik.http.routers.frontend.priority=1"
    - "traefik.http.services.frontend.loadbalancer.server.port=80"
Priority 1: Catch-all route (lowest priority).

Middleware

Middleware is defined in deploy/traefik/dynamic/middleware.yml.

CORS Middleware

Allows cross-origin requests from the frontend:
http:
  middlewares:
    cors:
      headers:
        accessControlAllowMethods:
          - "GET"
          - "POST"
          - "OPTIONS"
        accessControlAllowHeaders:
          - "Content-Type"
          - "Authorization"
          - "Connect-Protocol-Version"
          - "Connect-Timeout-Ms"
          - "Grpc-Timeout"
          - "X-Grpc-Web"
          - "X-User-Agent"
        accessControlAllowOriginList:
          - "http://localhost:30081"
        accessControlExposeHeaders:
          - "Grpc-Status"
          - "Grpc-Message"
          - "Grpc-Status-Details-Bin"
        accessControlMaxAge: 7200
        addVaryHeader: true
Key headers:
  • Connect-Protocol-Version: connect-rpc protocol version
  • Grpc-*: gRPC-Web headers
  • Idempotency-Key: For idempotent requests

Auth Middleware

Forwards authentication to the auth service:
auth:
  forwardAuth:
    address: "http://auth-service:8090/verify"
    authResponseHeaders:
      - "X-User-Id"
Flow:
  1. Traefik intercepts request
  2. Forwards Authorization header to http://auth-service:8090/verify
  3. If auth service returns 200, request proceeds with X-User-Id header added
  4. If auth service returns 401/403, request is rejected

Rate Limit Middleware

Limits requests per second:
rate-limit:
  rateLimit:
    average: 100
    burst: 50
  • average: 100 requests/second sustained
  • burst: Allow bursts up to 50 requests

Retry Middleware

Retries failed requests:
retry:
  retry:
    attempts: 3
    initialInterval: "100ms"
Retries up to 3 times with 100ms initial backoff.

Kubernetes Configuration

For Kubernetes deployment, Traefik uses CRDs (IngressRoute, Middleware) defined in Nixidy.

Helm Chart

From deploy/nixidy/env/traefik.nix:
helm.releases.traefik = {
  chart = charts.traefik.traefik;
  values = {
    image.tag = "v3.2.0";
    service.type = "NodePort";
    ports = {
      web.nodePort = 30081;
      websecure.nodePort = 30444;
    };
    providers = {
      kubernetesCRD.enabled = true;
      kubernetesIngress.enabled = true;
    };
    tracing.otlp.grpc = {
      enabled = true;
      endpoint = "otel-collector.observability:4317";
    };
  };
};
OpenTelemetry integration: Sends traces to the OTEL collector.

Middleware CRD

Example CORS middleware:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: cors-middleware
  namespace: microservices
spec:
  headers:
    accessControlAllowHeaders:
      - Content-Type
      - Authorization
      - Connect-Protocol-Version
    accessControlAllowMethods:
      - GET
      - POST
      - OPTIONS
    accessControlAllowOriginList:
      - http://localhost:5173
    accessControlMaxAge: 7200
    addVaryHeader: true

IngressRoute CRD

Example greeter route:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: greeter-route
  namespace: microservices
spec:
  entryPoints:
    - web
  routes:
    - kind: Rule
      match: PathPrefix(`/greeter.v1.GreeterService`)
      priority: 100
      middlewares:
        - name: cors-middleware
        - name: rate-limit-middleware
        - name: retry-middleware
      services:
        - name: greeter-service
          port: 80
          scheme: h2c

Accessing Traefik

Dashboard

Traefik dashboard is not enabled by default. To enable in development:
api:
  dashboard: true
  insecure: true
Access at http://localhost:30081/dashboard/

Debugging

Check Traefik Logs

Docker Compose:
docker compose logs -f traefik
Kubernetes:
kubectl logs -n edge deployment/traefik -f

List Routes

Docker Compose:
docker compose exec traefik traefik healthcheck
Kubernetes:
kubectl get ingressroute -n microservices
kubectl get middleware -n microservices

Test Endpoints

# Frontend
curl http://localhost:30081/

# Auth
curl http://localhost:30081/auth/health

# Greeter (gRPC)
grpcurl -plaintext -d '{"name": "World"}' \
  localhost:30081 greeter.v1.GreeterService/SayHello

Next Steps

Build docs developers (and LLMs) love