Skip to content

Traefik Reverse Proxy

Complete documentation for Traefik v3.6+ - the reverse proxy providing HTTPS access to all services on Bruno.


Overview

Property Value
Version Traefik v3.6+ (REQUIRED)
Container traefik
Server Bruno (188.34.198.57)
Ports 80 (HTTP→HTTPS redirect), 443 (HTTPS)
SSL Provider Let's Encrypt via TLS Challenge
Config Location /root/coder-core/services/production/

Version Requirement

You MUST use Traefik v3.6 or later on Ubuntu 24.04 with Docker 29.x.

Earlier versions cause the error:
```
client version 1.24 is too old. Minimum supported API version is 1.44
```

Traefik v3.6+ has automatic Docker API version negotiation which fixes this.

Architecture

Internet → Cloudflare → Port 443 → Traefik → Docker Container
                              Let's Encrypt (TLS Challenge)
  1. Request arrives at Cloudflare (proxied) or directly (DNS only)
  2. Traefik matches Docker container labels to find routing rules
  3. SSL certificate obtained automatically via Let's Encrypt TLS challenge
  4. Request proxied to target container's internal port
  5. Response returned through Traefik with HTTPS

Configuration

docker-compose.yml (Traefik Section)

From /root/coder-core/services/production/docker-compose.yml:

services:
  traefik:
    image: traefik:v3.6 # v3.6+ has auto Docker API version negotiation
    container_name: traefik
    restart: unless-stopped
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=dev@kua.cl"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.kua.cl`)"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"

Key points:

  • Uses command-line flags (not separate config files)
  • TLS challenge for certificates (not DNS challenge)
  • Auto HTTP→HTTPS redirect
  • Docker labels for discovery

Adding Services to Traefik

Add these labels to any service in docker-compose.yml:

services:
  myservice:
    image: myimage:latest
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myservice.rule=Host(`myservice.kua.cl`)"
      - "traefik.http.routers.myservice.entrypoints=websecure"
      - "traefik.http.routers.myservice.tls.certresolver=letsencrypt"
      - "traefik.http.services.myservice.loadbalancer.server.port=8080"

networks:
  proxy:
    driver: bridge

Required labels:

  1. traefik.enable=true - Opt-in to Traefik
  2. traefik.http.routers.<name>.rule=Host(...) - Domain routing
  3. traefik.http.routers.<name>.entrypoints=websecure - Use HTTPS
  4. traefik.http.routers.<name>.tls.certresolver=letsencrypt - SSL certificate
  5. traefik.http.services.<name>.loadbalancer.server.port=XX - Backend port

Currently Proxied Services

Service Domain Backend Port Container
Infisical secrets.kua.cl 8080 infisical
Forgejo git.kua.cl 3000 forgejo
Kuanary media.kua.cl, img-api.kua.cl 5000 kuanary
imgproxy cdn.kua.cl 8080 imgproxy
Obsidian notes.kua.cl 3000 obsidian
infra-docs docs.kua.cl 8000 infra-docs
Dashboard traefik.kua.cl Internal traefik

Monitoring

Check Traefik Status

# Container status
ssh root@188.34.198.57 "docker ps | grep traefik"

# Logs (check for errors)
ssh root@188.34.198.57 "docker logs --tail 50 traefik"

# Check for Docker API errors (should be NONE with v3.6+)
ssh root@188.34.198.57 "docker logs traefik 2>&1 | grep 'client version'"

Test SSL Certificate

# Check certificate details
curl -vI https://secrets.kua.cl 2>&1 | grep -E 'subject|issuer|SSL'

# Check expiration
echo | openssl s_client -connect secrets.kua.cl:443 2>/dev/null | openssl x509 -noout -dates

Troubleshooting

"client version 1.24 is too old"

Cause: Traefik version < v3.6 with Docker 29.x

Fix: Update Traefik image:

traefik:
  image: traefik:v3.6 # NOT v3.3 or earlier

Then redeploy:

ssh root@188.34.198.57 "cd /root/coder-core/services/production && docker compose pull traefik && docker compose up -d traefik"

ACME Certificate Errors

Symptoms: "Unable to obtain ACME certificate" in logs

Common causes:

  1. DNS not pointing to server - Update Cloudflare to point to Bruno (188.34.198.57)
  2. Rate limited - Wait 1 hour and retry (see Let's Encrypt rate limits)
  3. Cloudflare proxy blocking TLS challenge - For TLS challenge, domain must be DNS Only (gray cloud) OR use Cloudflare origin certificates

Fix for Cloudflare-proxied domains:

  • Either set domain to "DNS Only" temporarily during certificate issuance
  • Or switch to DNS challenge (requires Cloudflare API key)

Service Not Being Proxied (404)

Check:

  1. Service has traefik.enable=true label
  2. Service is on the proxy network
  3. Port matches container's exposed port
  4. Router rule matches the hostname exactly
# Inspect service labels
ssh root@188.34.198.57 "docker inspect <container> | jq '.[0].Config.Labels'"

# Check Traefik discovered the service
ssh root@188.34.198.57 "docker logs traefik 2>&1 | grep <service-name>"

Backup & Recovery

What to Backup

  • SSL Certificates: ./letsencrypt/acme.json (optional - can regenerate)
  • Configuration: Part of docker-compose.yml in Git

Recovery

Certificates regenerate automatically when Traefik starts. Just redeploy:

cd ~/coder-core/ansible
ansible-playbook playbooks/deploy-services.yml --limit bruno \
  -e "infisical_client_id=YOUR_ID" \
  -e "infisical_client_secret=YOUR_SECRET"

Security Best Practices

  1. Never expose Docker socket to the network - Only mount read-only locally
  2. Always use HTTPS - HTTP auto-redirects to HTTPS
  3. Keep Traefik updated - New versions fix security issues
  4. Protect dashboard - Add authentication middleware if exposing dashboard publicly


Last updated: January 2026 - Traefik v3.6+ on Bruno