Traefik Troubleshooting Reference¶
Comprehensive guide to diagnosing and resolving Traefik configuration and runtime issues.
Diagnostic Workflow¶
When something isn't working, follow this sequence:
1. Check if containers are running
└─→ docker ps
2. Check Traefik logs
└─→ docker logs traefik
3. Check container health
└─→ docker ps (look for "healthy")
4. Check Traefik dashboard
└─→ http://50.3.85.110:8080/dashboard/
5. Test network connectivity
└─→ docker network inspect
6. Check container logs
└─→ docker logs [container-name]
7. Test locally with curl
└─→ docker exec [container] wget ...
Category 1: Container Not Accessible (404 Errors)¶
Issue: Getting 404 errors when accessing domain¶
Symptoms: - Visiting http://app.egygeeks.com returns 404 - Traefik dashboard shows route exists - Container appears to be running
Root Causes & Solutions¶
Root Cause 1: Container is not healthy¶
Traefik only routes to containers with status "healthy".
Check container health:
Look at STATUS column:
STATUS
Up 2 minutes (healthy) ✅ Good - routes should work
Up 2 minutes (unhealthy) ❌ Bad - Traefik won't route here
Up 2 minutes ⚠️ No health check - still routes but not ideal
If unhealthy:
# Check what's failing
docker logs my-app
# Test health check manually
docker exec my-app wget -O- http://127.0.0.1:8000/
# Or with curl
docker exec my-app curl -v http://127.0.0.1:8000/
Fix the health check:
# ✅ Correct - use 127.0.0.1 in Alpine
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD wget --quiet --tries=1 --spider http://127.0.0.1:8000/ || exit 1
# ✅ Also correct - using curl
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD curl -f http://127.0.0.1:8000/ || exit 1
# ❌ May fail - localhost DNS resolution
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD wget http://localhost:8000/ || exit 1
Common reasons for unhealthy status:
- App not listening yet - Increase
start-period - App listening on wrong port - App is on 5000, health check checks 8000
- App only responds to specific path - Check
/healthinstead of/
# Example: App only has /api endpoint
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD wget -O- http://127.0.0.1:8000/api/health || exit 1
Root Cause 2: traefik.enable not set¶
Traefik ignores containers without traefik.enable=true.
Check container labels:
Fix: Add to docker-compose.yml:
services:
my-app:
labels:
- traefik.enable=true
- traefik.http.routers.my-app.rule=Host(`app.egygeeks.com`)
- traefik.http.services.my-app.loadbalancer.server.port=8000
Root Cause 3: Wrong port configuration¶
The service port doesn't match where your app is actually listening.
What your app reports:
But you configured:
Fix: Match the actual port:
Common port mistakes:
# ❌ Port 8000 is on HOST machine (not used)
ports:
- "8000:3000"
- traefik.http.services.my-app.loadbalancer.server.port=8000 # Wrong!
# ✅ Traefik uses container port (3000)
- traefik.http.services.my-app.loadbalancer.server.port=3000 # Correct!
To find the correct port:
# Check logs
docker logs my-app | grep -i "listening\|port\|started"
# Or try common ports
docker exec my-app curl http://127.0.0.1:3000/ # Node.js default
docker exec my-app curl http://127.0.0.1:5000/ # Flask default
docker exec my-app curl http://127.0.0.1:8000/ # Python/FastAPI default
docker exec my-app curl http://127.0.0.1:8080/ # Go default
Root Cause 4: Container not on traefik_public network¶
Container must be on the same network as Traefik.
Check networks:
Look for your container in the "Containers" section.
If not listed:
Or add it manually:
Root Cause 5: Router rule doesn't match request¶
You're accessing a different domain/path than the router expects.
Check your rule:
But you're visiting: http://50.3.85.110 or http://myapp.egygeeks.com
Fix: Update the rule or visit correct URL
# ✅ Match different hosts
- traefik.http.routers.my-app.rule=Host(`app.egygeeks.com`) || Host(`50.3.85.110`)
# ✅ Or route on IP with path
- traefik.http.routers.my-app.rule=Host(`50.3.85.110`) && PathPrefix(`/app`)
Verify router config:
Diagnostic Commands¶
# See all containers and their health
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# View container logs
docker logs --tail=50 -f my-app
# Test app directly inside container
docker exec my-app curl http://127.0.0.1:8000/
# Check Traefik sees the container
curl http://50.3.85.110:8080/api/http/services -s | jq '.'
# Check specific router config
curl http://50.3.85.110:8080/api/http/routers/my-app -s | jq '.'
# Test DNS (if using domain names)
docker exec my-app nslookup app.egygeeks.com
Category 2: SSL/HTTPS Certificate Issues¶
Issue: HTTPS not working or invalid certificate¶
Symptoms: - http://app.egygeeks.com works, but https://app.egygeeks.com doesn't - Browser shows "Not Secure" or certificate error - Certificates not being issued
Root Causes & Solutions¶
Root Cause 1: Wrong entrypoint configuration¶
HTTPS requires websecure entrypoint, not web.
Check current config:
Fix:
- traefik.http.routers.my-app.entrypoints=websecure # ✅ HTTPS
- traefik.http.routers.my-app.tls.certresolver=letsencrypt
Root Cause 2: Certificate resolver not configured¶
Even with websecure entrypoint, you need to specify which resolver to use.
Check config:
Fix:
- traefik.http.routers.my-app.entrypoints=websecure
- traefik.http.routers.my-app.tls.certresolver=letsencrypt
Root Cause 3: Not enough time for certificate provisioning¶
Let's Encrypt certificates take 30-60 seconds to obtain after first request.
Check current certificate status:
# View certificate file (if using Let's Encrypt)
cat /opt/traefik/acme.json
# Or check Traefik logs
docker logs traefik | grep -i "certificate\|acme\|let"
Solution: Wait 60 seconds and try again.
Root Cause 4: Port 443 not exposed¶
Traefik can't listen on HTTPS port.
Check Traefik port configuration:
If not:
Then restart Traefik:
Root Cause 5: Domain mismatch¶
Certificate issued for app.egygeeks.com, but accessing 50.3.85.110 or vice versa.
Check what domain Traefik will request certificate for:
This tells Let's Encrypt to issue a certificate for app.egygeeks.com.
Fix: Make sure you access via the same domain:
# ✅ Works - certificate issued for app.egygeeks.com
https://app.egygeeks.com
# ❌ Certificate error - certificate for app.egygeeks.com, not IP
https://50.3.85.110
Root Cause 6: acme.json permissions¶
Let's Encrypt certificate storage file has incorrect permissions.
Check permissions:
Fix permissions:
Diagnostic Commands¶
# Check HTTPS is responding
curl -I https://app.egygeeks.com
# Check certificate details
echo | openssl s_client -servername app.egygeeks.com -connect 50.3.85.110:443
# View certificate expiry
curl https://app.egygeeks.com:443 -vI 2>&1 | grep -i "expire\|valid"
# Check Traefik HTTPS configuration
curl http://50.3.85.110:8080/api/http/routers -s | jq '.[] | select(.Name=="my-app")'
# Check certificate in acme.json
cat /opt/traefik/acme.json | jq '.ACME.Certificates[] | {domain: .domain, certificate: .certificate}'
# View Traefik logs for certificate issues
docker logs traefik | grep -i -E "certificate|acme|tls|https"
Category 3: Path-Based Routing Issues¶
Issue: PathPrefix routing not working correctly¶
Symptoms: - Path-based routes not matching - Containers receiving wrong paths - 404 on path-based routes
Root Causes & Solutions¶
Root Cause 1: Missing StripPrefix middleware¶
Container expects / but receives /api/users.
Example:
# ❌ Missing middleware
- traefik.http.routers.api.rule=PathPrefix(`/api`)
- traefik.http.services.api.loadbalancer.server.port=3000
# Container receives: /api/users (wrong!)
Fix:
# ✅ Add StripPrefix middleware
- traefik.http.middlewares.api-strip.stripprefix.prefixes=/api
- traefik.http.routers.api.rule=PathPrefix(`/api`)
- traefik.http.routers.api.middlewares=api-strip
- traefik.http.services.api.loadbalancer.server.port=3000
# Container receives: /users (correct!)
Verify middleware is working:
# Check middleware configuration
curl http://50.3.85.110:8080/api/http/middlewares -s | jq '.'
# Check router has middleware applied
curl http://50.3.85.110:8080/api/http/routers/api -s | jq '.Middlewares'
Root Cause 2: Path doesn't match rule¶
Accessing /app/api but rule is PathPrefix(/api).
Check your rule:
But you're accessing: /app/api, /v1/api, or /api/v2
Verify what's being matched:
# Check all active routes
curl http://50.3.85.110:8080/api/http/routers -s | jq '.[] | {name: .Name, rule: .Rule}'
Fix: Update rule to match:
# Match /api and anything under it
- traefik.http.routers.api.rule=PathPrefix(`/api`)
# Match /app/api and anything under it
- traefik.http.routers.api.rule=PathPrefix(`/app/api`)
# Match multiple paths
- traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/v1/api`)
Root Cause 3: Routing order matters (specific before general)¶
Define more specific routes before general ones.
Problematic configuration:
# ❌ General path first
- traefik.http.routers.api.rule=PathPrefix(`/api`)
- traefik.http.services.api.loadbalancer.server.port=3000
# Then specific path (may not match!)
- traefik.http.routers.api-v2.rule=PathPrefix(`/api/v2`)
- traefik.http.services.api-v2.loadbalancer.server.port=3001
First route matches both /api and /api/v2!
Fix: Specific paths first
# ✅ Specific path first
- traefik.http.routers.api-v2.rule=PathPrefix(`/api/v2`)
- traefik.http.services.api-v2.loadbalancer.server.port=3001
# Then general path
- traefik.http.routers.api.rule=PathPrefix(`/api`)
- traefik.http.services.api.loadbalancer.server.port=3000
Now /api/v2 matches the first route, /api/users matches the second.
Root Cause 4: Container app expects different path structure¶
App is built for /app path, but Traefik strips it.
Example:
App has routing:
Traefik config:
- traefik.http.middlewares.app-strip.stripprefix.prefixes=/app
- traefik.http.routers.app.rule=PathPrefix(`/app`)
Result: Request /app/home becomes /home, but app expects /app/home.
Fix: Don't strip prefix if app needs it
# ❌ Don't strip if app needs the path
# - traefik.http.routers.app.middlewares=app-strip
# ✅ Keep the full path
- traefik.http.routers.app.rule=PathPrefix(`/app`)
Or rebuild app without the path prefix.
Diagnostic Commands¶
# See all routers and their rules
curl http://50.3.85.110:8080/api/http/routers -s | jq '.[] | {Name, Rule, Middlewares}'
# Test path matching
docker exec [container] curl -H "X-Forwarded-Uri: /api/users" http://127.0.0.1:8000/
# Check if middleware is stripping correctly
# Request /api/users and check what app receives
# (App must log incoming request paths)
Category 4: Service Discovery Issues¶
Issue: Traefik doesn't see containers or routes¶
Symptoms: - New containers don't appear in Traefik dashboard - Routes not updating when containers start/stop - "service not available" errors
Root Causes & Solutions¶
Root Cause 1: Container not on traefik_public network¶
Container must be connected to the network Traefik monitors.
Check:
Not listed? Container is not on the network.
Fix:
Then restart:
Or manually connect:
Root Cause 2: Traefik not watching Docker socket¶
Traefik needs access to Docker socket to discover containers.
Check Traefik container:
Should show volume mount.
Fix:
Restart Traefik:
Root Cause 3: Label syntax error¶
Typo in label name prevents Traefik from reading configuration.
Examples of typos:
# ❌ Missing underscore
- traefik.http.routers.my.app.rule=...
# ❌ Wrong component name
- traefik.http.router.my-app.rule=... # Should be "routers"
# ❌ Mixed case
- Traefik.http.routers.my-app.rule=... # Should be lowercase "traefik"
# ✅ Correct
- traefik.http.routers.my-app.rule=...
Validate labels:
docker inspect my-app | grep traefik
# Each should start with "traefik.http"
# Should not have typos or wrong case
Root Cause 4: Service name doesn't match router name¶
Service referenced by router must exist.
Example problem:
labels:
- traefik.http.routers.api.rule=Host(`api.egygeeks.com`)
# ❌ Missing service definition
# Container will route to non-existent service "api"
Fix: Add matching service
labels:
- traefik.http.routers.api.rule=Host(`api.egygeeks.com`)
- traefik.http.services.api.loadbalancer.server.port=3000
Or use different service name:
labels:
- traefik.http.routers.api.rule=Host(`api.egygeeks.com`)
# Explicitly reference existing service
# (Only needed if names don't match)
Root Cause 5: Docker daemon not accessible¶
Traefik container can't communicate with Docker daemon.
Check:
Verify volume mount:
If socket not mounted:
Restart:
Diagnostic Commands¶
# Check service discovery logs
docker logs -f traefik | grep -i "container\|service\|discover"
# List all discovered services
curl http://50.3.85.110:8080/api/http/services -s | jq '.[].Name'
# List all routers
curl http://50.3.85.110:8080/api/http/routers -s | jq '.[].Name'
# Check specific container labels
docker inspect my-app | grep -A 20 "Labels"
# Verify Docker socket is accessible in Traefik
docker exec traefik ls -la /var/run/docker.sock
Category 5: Performance & Load Balancing¶
Issue: Slow responses or uneven load distribution¶
Root Causes & Solutions¶
Root Cause 1: Health checks too strict¶
Container marked unhealthy, even though it's working.
Check health check configuration:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget --quiet --tries=1 --spider http://127.0.0.1:8000/
Parameters: - interval - How often to check (30s good) - timeout - How long before failing (3s too short if slow app) - start-period - Grace period before first check (5s may be too short)
Fix for slow apps:
# Increase timeout for slow responses
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s \
CMD wget --quiet --tries=1 --spider http://127.0.0.1:8000/
# Or check simpler endpoint
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD wget -O- http://127.0.0.1:8000/health || exit 1
Root Cause 2: Load balancer not configured¶
Multiple instances of same container, but Traefik sending all traffic to one.
Check service configuration:
Add load balancing:
# Round-robin (default, usually fine)
- traefik.http.services.app.loadbalancer.server.port=3000
# For multiple instances, Traefik auto-discovers and balances
services:
app-1:
labels:
- traefik.http.routers.app.rule=Host(`app.egygeeks.com`)
- traefik.http.services.app.loadbalancer.server.port=3000
app-2:
labels:
- traefik.http.routers.app.rule=Host(`app.egygeeks.com`)
- traefik.http.services.app.loadbalancer.server.port=3000
Root Cause 3: No compression middleware¶
Responses not compressed, large payloads slow.
Add compression:
labels:
- traefik.http.middlewares.compress.compress=true
- traefik.http.routers.app.middlewares=compress
Result: 60-80% smaller responses.
Root Cause 4: Unneeded middlewares slowing requests¶
Each middleware adds latency. Remove unnecessary ones.
Review middleware stack:
labels:
# Avoid chaining too many middlewares
- traefik.http.routers.app.middlewares=strip,auth,compress,ratelimit
# Each adds ~5-10ms latency
Optimize:
# Only use needed middlewares
- traefik.http.routers.app.middlewares=strip,compress
# Removed auth and ratelimit if not needed
Diagnostic Commands¶
# Check load distribution
curl http://50.3.85.110:8080/api/http/services/app -s | jq '.'
# Monitor response times
watch -n 1 'docker stats'
# Check if all instances healthy
docker ps --format "table {{.Names}}\t{{.Status}}"
# Monitor Traefik requests
docker logs -f traefik | grep -E "time_duration|Status"
Category 6: Docker Network Issues¶
Issue: Containers can't reach each other or Traefik¶
Symptoms: - Container can't reach other containers - "Name resolution failed" errors - Containers can't reach Traefik
Root Causes & Solutions¶
Root Cause 1: Containers on different networks¶
Services on internal network can't reach traefik_public network.
Check networks:
Shows all networks the container is on.
Fix: Add to both networks
services:
app:
networks:
- traefik_public # To access Traefik
- internal # To access database
database:
networks:
- internal # NOT on traefik_public
networks:
traefik_public:
external: true
internal:
driver: bridge
Root Cause 2: Container-to-container communication using wrong names¶
Containers can't resolve names unless on same network.
Wrong:
services:
app:
environment:
- DATABASE_URL=postgresql://mydb:5432/db
networks:
- traefik_public
database:
container_name: mydb
networks:
- internal
app can't reach mydb (on different networks, wrong name).
Fix:
services:
app:
environment:
- DATABASE_URL=postgresql://database:5432/db
depends_on:
- database
networks:
- traefik_public
- internal
database:
container_name: database
networks:
- internal
networks:
traefik_public:
external: true
internal:
driver: bridge
Now app reaches database (same network, correct name).
Root Cause 3: localhost vs container name¶
localhost won't work for inter-container communication.
Wrong:
environment:
- DATABASE_URL=postgresql://localhost:5432/db
# localhost = app container itself, not database
Correct:
Diagnostic Commands¶
# Check container networks
docker inspect my-app | jq '.NetworkSettings.Networks'
# Test container-to-container connectivity
docker exec app ping database
# Test name resolution
docker exec app nslookup database
# Test port connectivity
docker exec app nc -zv database 5432
# View network structure
docker network inspect traefik_public
docker network inspect internal
Category 7: Debugging Tools¶
Essential Commands¶
View Traefik configuration:
# See all routers
curl http://50.3.85.110:8080/api/http/routers -s | jq
# See all services
curl http://50.3.85.110:8080/api/http/services -s | jq
# See all middlewares
curl http://50.3.85.110:8080/api/http/middlewares -s | jq
# Check specific router
curl http://50.3.85.110:8080/api/http/routers/my-app -s | jq
Monitor logs:
# Traefik logs
docker logs -f traefik
# Container logs
docker logs -f my-app
# Both with timestamps
docker logs -f --timestamps traefik
# Last N lines
docker logs --tail=100 traefik
Test connectivity:
# Test container can reach endpoint
docker exec my-app curl -v http://app.egygeeks.com
# Test health check
docker exec my-app wget -O- http://127.0.0.1:8000/
# Test with specific host
docker exec my-app curl -H "Host: app.egygeeks.com" http://traefik:80/
# Dump response headers
docker exec my-app curl -i http://127.0.0.1:8000/
Inspect containers:
# Full container info
docker inspect my-app
# Just labels
docker inspect my-app | grep -A 50 "Labels"
# Just networks
docker inspect my-app | grep -A 10 "NetworkSettings"
# Just health
docker inspect my-app | grep -A 5 "Health"
Category 8: Common Error Messages¶
404 Not Found¶
Meaning: Request matched no router or service.
Check: 1. Router rule matches your request 2. Container is healthy 3. Service port is correct
502 Bad Gateway¶
Meaning: Traefik reached the service but got error.
Check: 1. Is container healthy? 2. Is app listening on configured port? 3. Are there application-level errors?
503 Service Unavailable¶
Meaning: No healthy instances available.
Check:
Connection Refused¶
Meaning: Can't connect to container.
Check: 1. Is container running? 2. Is port correct? 3. Is app actually listening?
Network Unreachable¶
Meaning: Container not on same network as target.
Check:
Quick Reference Checklist¶
When something breaks:
-
docker ps- Are containers running and healthy? -
docker logs traefik- Any Traefik errors? -
docker logs my-app- Any app errors? -
curl http://50.3.85.110:8080/api/http/routers- Routes exist? - Check labels syntax - Any typos?
- Container on
traefik_publicnetwork? - Service port matches app's actual port?
- Router rule matches your request?
- Health check passing?
Related Documentation¶
- Overview - Architecture and concepts: overview.md
- Labels Reference - All label options: labels.md
- Templates - Ready-to-use configurations: Templates
For how-to guides on deploying with Traefik, see the Journey guides.