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¶
- Request arrives at Cloudflare (proxied) or directly (DNS only)
- Traefik matches Docker container labels to find routing rules
- SSL certificate obtained automatically via Let's Encrypt TLS challenge
- Request proxied to target container's internal port
- 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:
traefik.enable=true- Opt-in to Traefiktraefik.http.routers.<name>.rule=Host(...)- Domain routingtraefik.http.routers.<name>.entrypoints=websecure- Use HTTPStraefik.http.routers.<name>.tls.certresolver=letsencrypt- SSL certificatetraefik.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:
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:
- DNS not pointing to server - Update Cloudflare to point to Bruno (188.34.198.57)
- Rate limited - Wait 1 hour and retry (see Let's Encrypt rate limits)
- 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:
- Service has
traefik.enable=truelabel - Service is on the
proxynetwork - Port matches container's exposed port
- 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.ymlin 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¶
- Never expose Docker socket to the network - Only mount read-only locally
- Always use HTTPS - HTTP auto-redirects to HTTPS
- Keep Traefik updated - New versions fix security issues
- Protect dashboard - Add authentication middleware if exposing dashboard publicly
Related Documentation¶
- Hetzner VPS Overview - Server details
- Services Overview - All production services
- Provisioning Protocol - How to provision new servers
- Troubleshooting - Common issues
Last updated: January 2026 - Traefik v3.6+ on Bruno