Why Traefik Beats a Manual Nginx Config Every Time
Running multiple Docker containers on a single server creates an immediate routing problem. You have a dozen services, each exposed on a different port, and no clean way to direct traffic from a single domain to the right container without maintaining a sprawling, error-prone configuration file. Traefik solves this by reading Docker labels directly from your containers and building its own routing rules on the fly – no manual reloads, no static config blocks for every new service you spin up.
Traefik is a cloud-native reverse proxy and load balancer built specifically for dynamic environments. Unlike Nginx or Apache, which require you to write and reload configuration files each time you add a service, Traefik watches your Docker socket in real time. The moment a container starts with the right labels, Traefik picks it up and starts routing traffic to it. The moment that container stops, the route disappears automatically.
It also handles SSL certificates from Let’s Encrypt out of the box.

What You Need Before You Start
This guide assumes you have a Linux server (a VPS or a home lab machine both work fine) with Docker and Docker Compose installed. You also need a domain name pointed at your server’s public IP address. Traefik will use that domain to request SSL certificates from Let’s Encrypt, so DNS needs to be resolving correctly before you run anything. If you’re running this locally without a public domain, you can still follow along – just skip the HTTPS sections and use HTTP for testing.
You’ll need two DNS records at minimum: one for your root domain and one wildcard or specific subdomain for Traefik’s own dashboard, such as traefik.yourdomain.com. The dashboard is optional but worth enabling during setup so you can visually confirm that Traefik is detecting your containers and routing correctly. Port 80 and 443 need to be open on your firewall, and port 8080 if you want the dashboard accessible locally.
Create a working directory for your Traefik setup – something like /opt/traefik – and inside it, create two files: docker-compose.yml and traefik.yml. You’ll also need an empty file called acme.json with permissions set to 600. That file is where Traefik stores its Let’s Encrypt certificate data, and it will refuse to start if the permissions are wrong.
Configuring Traefik and Launching It
The static configuration lives in traefik.yml. This is where you define your entrypoints, enable the Docker provider, and configure the Let’s Encrypt certificate resolver. A minimal working config looks like this: set entrypoints for web (port 80) and websecure (port 443), enable the Docker provider with exposedByDefault: false so Traefik ignores containers that don’t explicitly opt in, and configure the ACME resolver with your email address and the acme.json storage path. Setting exposedByDefault to false is worth emphasizing – without it, every container on your server gets exposed automatically, which is a security problem you don’t want.

Your docker-compose.yml for the Traefik container itself needs to mount the Docker socket so Traefik can watch for container events, mount the traefik.yml config file, and mount acme.json for certificate storage. The container should also be connected to an external Docker network – create one with docker network create traefik-proxy before running anything. Any service you want Traefik to route traffic to must also be on this network. Labels on the Traefik container itself handle the dashboard routing and basic auth if you want to protect it with a password.
Once both files are ready, run docker compose up -d from your Traefik directory. Check logs with docker compose logs -f to confirm it starts without errors and successfully requests a certificate from Let’s Encrypt. The first certificate request can take 30 to 60 seconds. If you see errors about port 80 being in use, something else on your server is binding that port – typically an existing Nginx install or Apache. Stop that service first. If you see ACME errors, double-check that your domain’s DNS is resolving to your server’s IP and that port 80 is reachable from the outside, since Let’s Encrypt needs to make an HTTP-01 challenge request to verify domain ownership.
Routing Your First Container Through Traefik
Adding a service to Traefik routing is done entirely through Docker labels in that service’s own docker-compose.yml. No changes needed to Traefik’s config. The required labels are: traefik.enable=true to opt the container in, a router rule using traefik.http.routers.yourservice.rule=Host(`yourservice.yourdomain.com`), an entrypoint set to websecure, a TLS cert resolver pointing to your ACME config, and a service port label so Traefik knows which internal port the container is listening on. The container also needs to be connected to the traefik-proxy network.
A common mistake at this stage is forgetting to add the external network reference in the service’s compose file. If the container is on a different Docker network than Traefik, the proxy can see the container’s labels but can’t actually reach the container to forward traffic – you’ll get a “bad gateway” error in the browser. Adding the network and restarting the container fixes it immediately. Another issue that comes up is port conflicts on the service port label: if your app runs on port 3000 internally but you forget to specify that with the traefik.http.services.yourservice.loadbalancer.server.port label, Traefik defaults to port 80 and the connection fails silently.
For self-hosted tools that benefit from this kind of routing setup – like Seafile, which runs multiple internal services across different ports – Traefik’s label-based routing is far cleaner than managing individual Nginx server blocks. Each service gets its own subdomain, its own certificate, and its own router definition without touching a shared config file.

Making It Production-Ready
Traefik’s middleware system is where the real control lives. You can attach rate limiting, IP whitelisting, redirect rules, and basic authentication to any router without touching the container itself – just add middleware labels. For the dashboard specifically, always put it behind at least basic auth or restrict it by IP. Leaving the dashboard open on a public server exposes your full routing map to anyone who finds it. The traefik.http.middlewares.auth.basicauth.users label accepts a htpasswd-formatted user string, and you can generate one with htpasswd -nb username password – just make sure to escape dollar signs in the compose file by doubling them, or the password hash will break silently and lock you out.
Automatic HTTPS redirects are a one-line middleware: create a redirect middleware that sends all HTTP traffic to the HTTPS entrypoint, then attach it to a catch-all HTTP router. From that point on, anything hitting port 80 gets pushed to 443 without any per-service configuration. Certificate renewals are fully automatic – Traefik checks expiry dates and renews through Let’s Encrypt before they expire, writing the updated certificate back to acme.json. The only thing that can break renewals is a permissions change on that file or a firewall rule that blocks Let’s Encrypt’s challenge requests. Keep a cron job or a monitoring alert on certificate expiry dates as a backup, because if acme.json gets corrupted, Traefik does not always surface the error clearly in logs.





