DockerHomeLab
networking

Traefik + Docker Compose: Automatic HTTPS for Every Service

Set up Traefik as a reverse proxy with Docker Compose and get automatic Let's Encrypt HTTPS certificates for every service on your homelab.

By Editorial · · 7 min read

Running multiple Docker services on the same machine means dealing with ports: Nextcloud on 8080, Vaultwarden on 8081, Jellyfin on 8096. Remembering port numbers is annoying, but worse, most services require HTTPS to work correctly — and getting a certificate for each service individually is a pain.

Traefik solves both problems. It watches your Docker containers automatically, routes traffic based on labels in your Compose files, and provisions Let’s Encrypt certificates without any manual intervention.

This guide sets up a working Traefik installation that you can use as the front door for everything else on your homelab.

Prerequisites

If you’re running on a local-only network without a public domain, use a wildcard DNS service (like nip.io) or self-signed certificates instead. This guide focuses on the public HTTPS path.

Architecture

Traefik runs as a container on a Docker network shared with your other services. When you start a new service container with the right labels, Traefik detects it automatically via the Docker socket and starts routing traffic to it.

Internet → Port 443 → Traefik → your-service.yourdomain.com → service container

Step 1: Create the Traefik Network

All services that Traefik will proxy need to be on a shared network. Create it once:

docker network create traefik-proxy

Step 2: Create the Traefik Configuration Directory

mkdir -p ~/services/traefik
cd ~/services/traefik
touch acme.json
chmod 600 acme.json

The acme.json file stores your Let’s Encrypt certificates. The chmod 600 is required — Traefik will refuse to start if the file has looser permissions.

Step 3: Write the Traefik Compose File

Create ~/services/traefik/docker-compose.yml:

services:
  traefik:
    image: traefik:v3
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./acme.json:/acme.json
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=you@yourdomain.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/acme.json"
    networks:
      - traefik-proxy

networks:
  traefik-proxy:
    external: true

Replace you@yourdomain.com with your actual email. Let’s Encrypt uses it for expiry notifications.

Key flags:

Step 4: Start Traefik

docker compose up -d

Check that it started:

docker compose logs -f

You should see Traefik start up and detect the Docker provider. No certificate activity yet — that happens when a service requests one.

Step 5: Add Traefik Labels to a Service

Here’s how to set up Vaultwarden behind Traefik. Create ~/services/vaultwarden/docker-compose.yml:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    volumes:
      - ./data:/data
    environment:
      - WEBSOCKET_ENABLED=true
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.rule=Host(`vault.yourdomain.com`)"
      - "traefik.http.routers.vaultwarden.entrypoints=websecure"
      - "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
    networks:
      - traefik-proxy

networks:
  traefik-proxy:
    external: true

Note: no ports: section — Traefik handles the routing. The container is only reachable via Traefik.

Start it:

docker compose up -d

Traefik detects the new container via the Docker socket, reads the labels, and starts routing vault.yourdomain.com to the Vaultwarden container. It requests a certificate from Let’s Encrypt on first access — expect a few seconds delay the first time.

Step 6: Verify

Navigate to https://vault.yourdomain.com. You should see:

If it doesn’t work, check:

# Traefik logs
docker logs traefik -f

# Verify the container is on the traefik-proxy network
docker inspect vaultwarden | grep -A 5 Networks

Adding More Services

Every additional service follows the same pattern: add the traefik-proxy network, add four labels (enable, rule, entrypoints, tls), remove the ports: section. No changes to Traefik itself.

For services that listen on a non-80 port inside the container, set the loadbalancer.server.port label accordingly. For example, Jellyfin listens on port 8096 inside its container:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.jellyfin.rule=Host(`media.yourdomain.com`)"
  - "traefik.http.routers.jellyfin.entrypoints=websecure"
  - "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
  - "traefik.http.services.jellyfin.loadbalancer.server.port=8096"

Traefik Dashboard

Traefik includes a read-only dashboard showing all detected routers, services, and certificates. Add this to the Traefik command list to enable it:

- "--api.dashboard=true"

Then add a router to expose it:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
  - "traefik.http.routers.dashboard.entrypoints=websecure"
  - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
  - "traefik.http.routers.dashboard.service=api@internal"

Restrict access to the dashboard — it exposes your routing configuration. Adding basic auth middleware or IP allowlisting is recommended before exposing it.

#traefik #docker-compose #reverse-proxy #https #letsencrypt #homelab

Related

Comments