Follow the Journey
3 min read

Traefik as the Glue

Reverse proxy, subdomains, SSL

I wasted a week on nginx before switching to Traefik.

That week still haunts me.

The nginx nightmare

Every time I added a product, I had to:

  1. Add a new server block to nginx.conf
  2. Configure the upstream
  3. Reload nginx
  4. Hope I didn't break everything

With 31 products, that's 31 server blocks. 31 upstreams. 31 chances to make a typo.

I tried templating. I tried automation. It still felt fragile.

There had to be a better way.

Traefik: configure nothing

Traefik reads Docker labels. That's it. No config files to maintain.

# docker-compose.yml
recall:
  image: brainzllc/recall:latest
  labels:
    - "traefik.http.routers.recall.rule=Host(`recall.localhost`)"
    - "traefik.http.services.recall.loadbalancer.server.port=3000"

Add a service. Add labels. Traefik picks it up automatically.

When I add product #32, I don't touch any Traefik config. I add labels to the service. Done.

How it actually works

Traefik sits in front of everything:

Internet
    ↓
┌─────────┐
│ Traefik │  ← Reads Docker labels
└────┬────┘
     ├── recall.localhost    → Recall (3001)
     ├── reflex.localhost    → Reflex (3002)
     ├── vault.localhost     → Vault (3006)
     └── pulse.localhost     → Pulse (3003)

Request comes in. Traefik checks the Host header. Routes to the right service. Simple.

Local development magic

For local development, .localhost domains route to 127.0.0.1 in most browsers.

But Safari is stubborn (of course it is). So I add entries to /etc/hosts:

127.0.0.1 recall.localhost reflex.localhost pulse.localhost
127.0.0.1 vault.localhost signal.localhost flux.localhost

One-time setup. Now every subdomain works everywhere.

Production: SSL for free

Traefik handles Let's Encrypt automatically:

# traefik.yml
certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

Point DNS at the server. Traefik gets certificates. HTTPS just works.

No certbot cron jobs. No manual renewals. No forgetting and having everything break at 3am.

I've been burned by expired certificates before. Never again.

The dashboard

Traefik has a built-in dashboard at port 8080:

  • See all active routes
  • Check service health
  • Debug "why isn't this request reaching my service?"

When something's not working, the dashboard usually shows why in seconds. Green means healthy. Red means problem. Simple.

I keep it open in a tab during development.

Health checks

Traefik can check if services are healthy before routing to them:

recall:
  labels:
    - "traefik.http.services.recall.loadbalancer.healthcheck.path=/up"
    - "traefik.http.services.recall.loadbalancer.healthcheck.interval=10s"

If Recall is down, Traefik stops sending traffic to it. When it comes back up, traffic resumes automatically.

For a solo founder, this is huge. I can't monitor everything 24/7. Traefik can.

Why not nginx?

I've used nginx for 10 years. It's rock solid. But for this project:

Dynamic discovery wins. Docker labels beat config files. Add a service, it just works.

Built for containers. Traefik understands Docker natively. Nginx needs plugins and workarounds.

Modern defaults. HTTP/2, WebSockets, gRPC—all work out of the box. No tuning.

For traditional deployments, nginx is still great. For Docker-native stacks with lots of services, Traefik is better.

The full picture

Here's how a request flows through the system:

User types recall.localhost
    ↓
Browser resolves to 127.0.0.1
    ↓
Traefik receives request on port 80
    ↓
Traefik matches Host("recall.localhost")
    ↓
Traefik checks health of Recall service
    ↓
Traefik forwards to Recall on port 3000
    ↓
Recall processes and responds
    ↓
Response flows back through Traefik

All automatic. All configured via Docker labels. All visible in the dashboard.

This is the kind of infrastructure I dreamed about 10 years ago.

— Andres

All posts Follow along

Want to follow the journey?

Get Updates