Traefik Reverse Proxy Setup with Automatic HTTPS for Multiple Applications

Traefik Reverse Proxy Setup with Automatic HTTPS for Multiple Applications

We earn commissions when you shop through the links on this page, at no additional cost to you.

When I started running multiple self-hosted applications on my homelab, I quickly realized that managing SSL certificates and routing traffic was becoming a nightmare. Nginx and Caddy are great, but I wanted something that could dynamically detect new containers and automatically provision HTTPS certificates without manual intervention. That's where Traefik changed everything for me.

Traefik is a modern reverse proxy and load balancer built for containerized environments. Unlike traditional reverse proxies that require you to edit config files and reload services, Traefik watches your Docker daemon, reads labels from running containers, and automatically updates routing rules in real-time. Combined with Let's Encrypt integration, you get free SSL certificates provisioned automatically. In this guide, I'll walk you through a production-ready Traefik setup that serves multiple applications with zero manual certificate management.

Why I Chose Traefik Over Nginx Proxy Manager

I spent three months running Nginx Proxy Manager (NPM) before switching to Traefik. NPM was simple and had a nice web UI, but every time I spun up a new Docker service, I had to manually add a proxy host, configure SSL, and restart the service. With a homelab that changes constantly, this became tedious. Traefik eliminated that friction entirely—I just add labels to my docker-compose files and Traefik handles everything automatically.

The learning curve was steeper initially, but the payoff has been massive. I now manage 12+ self-hosted applications (Nextcloud, Jellyfin, Gitea, Vaultwarden, AdGuard Home, Open WebUI, and more) all behind one Traefik instance with zero manual intervention.

Prerequisites and Architecture

You'll need:

The architecture I use is straightforward: Traefik listens on ports 80 and 443, accepts incoming requests, reads container labels to determine routing rules, and forwards traffic to backend services. Let's Encrypt certificates are stored on disk and renewed automatically before expiration.

Core Traefik Docker Compose Setup

Here's the complete docker-compose.yml file I use in production. This creates the Traefik container with Let's Encrypt support and proper configuration:

version: '3.8'

services:
  traefik:
    image: traefik:v3.1.0
    container_name: traefik
    restart: always
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    environment:
      - CLOUDFLARE_EMAIL=${CLOUDFLARE_EMAIL}
      - CLOUDFLARE_API_KEY=${CLOUDFLARE_API_KEY}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/traefik.yml:ro
      - ./acme.json:/acme.json
      - ./config.yml:/config.yml:ro
    networks:
      - traefik-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.yourdomain.com`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=auth@file"
      - "traefik.http.services.traefik.loadbalancer.server.port=8080"

networks:
  traefik-network:
    driver: bridge

Before running this, create the acme.json file and set correct permissions (Let's Encrypt certificate storage):

touch acme.json
chmod 600 acme.json

Main Traefik Configuration File

Create traefik.yml in the same directory. This file defines entry points, certificate resolvers, and core behavior:

global:
  checkNewVersion: true
  sendAnonymousUsage: false

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          regex: "^http://(.*)"
          replacement: "https://${1}"
          permanent: true
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt
        domains:
          - main: yourdomain.com
            sans:
              - "*.yourdomain.com"

certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
      storage: acme.json
      httpChallenge:
        entryPoint: web
      # Uncomment for Cloudflare DNS challenge (better for wildcards)
      # dnsChallenge:
      #   provider: cloudflare
      #   resolvers:
      #     - "1.1.1.1:53"
      #     - "8.8.8.8:53"

api:
  dashboard: true
  insecure: false

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: traefik-network
  file:
    filename: /config.yml
    watch: true

log:
  level: INFO
  filePath: /var/log/traefik/traefik.log

accessLog:
  filePath: /var/log/traefik/access.log
Watch out: The HTTP challenge method works well for standard domains, but if you need wildcard certificates (*.yourdomain.com), use DNS challenge instead. I switched to Cloudflare DNS challenge after experiencing Let's Encrypt rate limits with HTTP-only setups.

Basic HTTP Authentication for Dashboard

The dashboard is exposed on port 8080, but we should protect it. Create config.yml with basic auth middleware:

http:
  middlewares:
    auth:
      basicAuth:
        users:
          - "admin:$apr1$abc123def456$xxxxxxxxxxxx"

  routers:
    http-catchall:
      rule: "HostRegexp(`{host:.+}`)"
      entryPoints:
        - web
      middlewares:
        - redirect-to-https
      service: noop

  services:
    noop:
      loadBalancer:
        servers:
          - url: "http://localhost"

  middlewares:
    redirect-to-https:
      redirectScheme:
        scheme: https
        permanent: true

Generate the basicAuth hash using htpasswd (install with apt-get install apache2-utils):

htpasswd -c /dev/stdout admin | cut -d: -f2
# Enter password when prompted, copy the hash to config.yml

Deploying Applications Behind Traefik

Now the magic happens. When you deploy any Docker container, just add labels and Traefik automatically routes traffic to it. Here's a real example—deploying Nextcloud behind Traefik:

version: '3.8'

services:
  nextcloud-db:
    image: postgres:15
    container_name: nextcloud-db
    environment:
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=secure_password_here
    volumes:
      - nextcloud-db:/var/lib/postgresql/data
    networks:
      - traefik-network
    restart: always

  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud
    depends_on:
      - nextcloud-db
    environment:
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=secure_password_here
      - POSTGRES_HOST=nextcloud-db
      - TRUSTED_PROXIES=traefik
      - OVERWRITEPROTOCOL=https
      - OVERWRITEHOST=nextcloud.yourdomain.com
    volumes:
      - nextcloud-data:/var/www/html
    networks:
      - traefik-network
    restart: always
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.yourdomain.com`)"
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
      - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
      - "traefik.http.middlewares.nextcloud-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.nextcloud.middlewares=nextcloud-headers@docker"

volumes:
  nextcloud-db:
  nextcloud-data:

networks:
  traefik-network:
    external: true

Notice the key labels: traefik.enable=true tells Traefik to manage this container, the rule defines the hostname, and the entrypoints/tls/certresolver settings handle HTTPS automatically. When you deploy this, Traefik instantly provisions an SSL certificate for nextcloud.yourdomain.com and routes all traffic there.

Advanced Features I Use Daily

Path-Based Routing

Not every app needs its own domain. I route multiple applications through subpaths instead. For example, serving Vaultwarden at yourdomain.com/vault:

labels:
  - "traefik.http.routers.vaultwarden.rule=Host(`yourdomain.com`) && Path(`/vault`)"
  - "traefik.http.routers.vaultwarden.middlewares=strip-vault@docker"
  - "traefik.http.middlewares.strip-vault.stripprefix.prefixes=/vault"

Rate Limiting

Protect services from brute force attacks using Traefik rate limiting middleware:

labels:
  - "traefik.http.middlewares.ratelimit.ratelimit.average=100"
  - "traefik.http.middlewares.ratelimit.ratelimit.burst=50"
  - "traefik.http.routers.myapp.middlewares=ratelimit@docker"

Monitoring with Metrics

Traefik exposes Prometheus metrics on the dashboard. I scrape these with my monitoring stack to track request counts, latency, and error rates across all proxied services.

Tip: Add this to traefik.yml to enable Prometheus metrics: metrics: prometheus: addEntryPointsLabels: true addServicesLabels: true

Security Best Practices I Implement

Never expose the Docker socket directly without sandboxing. I run Traefik in a dedicated container with minimal privileges. Always use strong basicAuth passwords for the dashboard—use a password manager to generate complex credentials. Enable HTTP/2 and TLS 1.3 by default (Traefik does this automatically with modern Let's Encrypt certs).

For sensitive applications, I add an extra authentication layer using Authelia middleware, which provides SSO and TOTP support across all proxied services.

Troubleshooting Common Issues

If certificates aren't provisioning, check the logs: docker logs traefik. Most commonly, DNS isn't resolving your domain correctly, or ports 80/443 aren't accessible from the internet. Verify with: curl -I http://yourdomain.com

If Traefik can't reach your backend service, confirm the container is on the same Docker network (traefik-network in our setup) and the service port is correct in the labels.

Rate limiting issues? Traefik counts requests per IP, so if you're behind a CDN like Cloudflare, configure X-Forwarded-For headers properly.

Next Steps

Start with the basic setup above and add applications one by one using the Nextcloud example as a template. Once you're comfortable, explore Traefik's middleware ecosystem—compression, circuit breakers, custom headers, and more are available through simple labels. Consider setting up monitoring alerts for certificate renewal failures, and test your wildcard certificate setup before relying on it for production services.

If you're running this on a budget, I recommend RackNerd's KVM VPS plans, which offer excellent performance for homelab and self-hosted workloads at a fraction of mainstream provider costs.