
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Traefik: Modern Reverse Proxy for Docker eliminates manual configuration reloading by listening directly to the Docker socket and updating routes as containers start or stop. Unlike traditional proxies that require static config files and restart scripts, this cloud-native edge router treats infrastructure as dynamic state rather than fixed definitions. For teams running microservices on Docker Compose or Kubernetes, adopting an auto-discovering proxy reduces deployment friction and removes an entire class of configuration drift errors.
How does Traefik: Modern Reverse Proxy for Docker handle service discovery?
The core differentiator is the provider model. Instead of parsing static configuration files on startup, Traefik watches the Docker API (or Kubernetes API) for events. When a container starts with specific labels, Traefik detects the event, extracts the routing metadata, and hot-reloads its internal routing table within milliseconds. No SIGHUP signal, no reload script, no downtime.
In practice, this means your routing configuration lives alongside your application definition. A common mistake I see in Nepal-based startups migrating from shared hosting is maintaining separate Nginx configs in a different repository. With Traefik, the route is the label. If you deploy a new version of your Laravel app with updated labels, the route updates atomically with the container lifecycle.
Understanding entrypoints and routers
Entrypoints define where traffic enters (ports 80, 443, 8080). Routers match incoming requests against host headers, paths, or methods, then forward them to services. Middleware sits between router and service, handling authentication, rate limiting, or header manipulation. This separation keeps concerns isolated and testable.
How do you configure Traefik with Docker Compose securely?
Security must be baked into the base configuration, not bolted on later. The following setup assumes you have already completed Docker installation on Ubuntu and understand basic compose syntax. Never expose the Docker socket to untrusted containers; mount it read-only and restrict network access.
version: "3.9"
services:
traefik:
image: traefik:v3.1
container_name: traefik-proxy
restart: unless-stopped
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./acme.json:/acme.json
- ./traefik.yml:/etc/traefik/traefik.yml:ro
ports:
- "80:80"
- "443:443"
networks:
- proxy-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=auth-basic"
- "traefik.http.middlewares.auth-basic.basicauth.users=admin:$$apr1$$xyz..."
networks:
proxy-net:
driver: bridge The read_only: true flag prevents runtime filesystem writes except to explicitly mounted tmpfs or volumes. The no-new-privileges option blocks privilege escalation attacks. These are non-negotiable in any environment handling PII or financial data, especially for fintech deployments where data protection compliance matters.
Static versus dynamic configuration
Split configuration deliberately. Static config (traefik.yml) defines entrypoints, providers, and ACME resolvers — things that change rarely. Dynamic config (labels or file provider) defines routes, services, and middleware. Mixing these leads to painful restart cycles. Keep static config minimal and version-controlled separately from application compose files.
How does automatic TLS work with Let's Encrypt in Traefik?
Traefik integrates ACME natively, eliminating certbot sidecars and cron jobs. Certificates are requested, validated, stored, and renewed automatically. The critical detail most tutorials miss: acme.json must have permissions set to 600 before Traefik starts, or it will refuse to write certificates as a safety measure.
# traefik.yml (static config excerpt)
certificatesResolvers:
le:
acme:
email: [email protected]
storage: /acme.json
httpChallenge:
entryPoint: web
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: le This configuration forces HTTP-to-HTTPS redirection globally at the entrypoint level. Individual services inherit this behavior unless explicitly overridden. For wildcard certificates or environments behind Cloudflare, switch to dnsChallenge with appropriate provider credentials stored in Docker secrets, never in plaintext labels.
Certificate renewal and monitoring
Traefik checks expiry daily and renews when certificates fall below 30 days validity. Monitor this via the built-in metrics endpoint or integrate with Prometheus metrics fundamentals to alert on certificate age. Silent renewal failures are the most common cause of unexpected outages in self-hosted setups.
Traefik vs Nginx: Which reverse proxy should you choose for containers?
Both are production-grade, but they solve different problems. Choose based on operational model, not benchmarks. If your team manages infrastructure as code and deploys multiple times daily, Traefik’s native integration wins. If you need maximum raw throughput for a single monolithic app with stable routing, Nginx remains excellent.
| Criteria | Traefik v3 | Nginx |
|---|---|---|
| Configuration Model | Declarative labels + auto-discovery | Static config files + reload |
| TLS Automation | Built-in ACME, zero external tools | Requires certbot/sidecar + cron |
| Dashboard & Observability | Native UI + Prometheus metrics | Requires third-party modules |
| Kubernetes Integration | First-class Ingress Controller | Via NGINX Ingress Controller |
| Raw Throughput (static) | Good (~85-90% of Nginx) | Excellent (baseline) |
| Learning Curve | Moderate (new concepts) | Low (well-documented legacy) |
| Middleware Ecosystem | Built-in auth, rate-limit, circuit breaker | Lua/njs scripting or modules |
In my experience helping Nepali SMEs adopt cloud-native stacks, teams switching from shared hosting consistently prefer Traefik once they understand labels. The mental model shift from "edit config file" to "annotate deployment" aligns better with GitOps workflows and GitOps with ArgoCD patterns. Reserve Nginx for edge caching layers or legacy apps where routing never changes.
What are common production pitfalls and how do you avoid them?
After auditing dozens of deployments, these issues recur constantly:
- Docker socket exposure: Always mount read-only (
:ro). Never run Traefik as root in production; use user namespaces or rootless Docker if possible. - Missing network isolation: Create a dedicated
proxy-netnetwork. Services not on this network cannot be routed, preventing accidental exposure of internal databases or admin panels. - Label typos: Traefik silently ignores malformed labels. Enable
--log.level=DEBUGduring initial setup, then downgrade to WARN. Usetraefik show-configCLI to validate parsed configuration. - ACME permission errors: Run
touch acme.json && chmod 600 acme.jsonbefore first start. Traefik refuses to create this file itself as a security precaution. - Resource limits omitted: Set CPU/memory requests and limits. Unbounded proxies can starve application containers during traffic spikes, violating resource limit best practices.
Observability integration
Enable Prometheus metrics in static config and scrape from your monitoring stack. Combine with structured logging in JSON format for centralized aggregation. Without observability, you cannot distinguish routing failures from application failures during incidents.
Deploying Traefik: Modern Reverse Proxy for Docker in Production
Start with the secure baseline configuration above, add your first service with labels, verify TLS issuance, then layer middleware incrementally. Treat Traefik configuration as infrastructure code: version control it, review changes in pull requests, and test in staging before production. If your team needs help designing a compliant, observable container platform, reach out to discuss your architecture. Getting the proxy layer right prevents months of debugging downstream.