πŸ“
Home Lab

Traefik v3 Reverse Proxy on Proxmox LXC: HTTPS for Every Self-Hosted Service in 2026

Ricardo Gil
July 27, 2026
6 min read
#Proxmox #Traefik #Self-Hosting #Reverse Proxy #LXC

If you're running more than three self-hosted services, you've hit the port-hell wall. Jellyfin on 8096, Immich on 2283, Forgejo on 3000 β€” remembering which port maps to which service stops being cute fast. Traefik v3 solves this cleanly: one LXC container handles all ingress, terminates TLS, and routes by hostname automatically. No nginx blocks to hand-edit every time you add a service.

This is a full walkthrough: creating the LXC, configuring Traefik v3 with the Docker and file providers, wiring up Let's Encrypt with a Cloudflare DNS-01 challenge, and adding a middleware stack worth keeping. By the end, every service gets a real HTTPS subdomain with auto-renewing certs β€” no self-signed anything.

Why Traefik Over Nginx Proxy Manager?

NPM is fine for beginners. Traefik is better for engineers. The key difference: Traefik detects new Docker containers and reconfigures routing automatically via labels β€” no UI clicks, no manual config edits. You define the route in your docker-compose.yml and it's live. The file provider handles non-Docker services (bare LXCs, VMs, anything with an IP). Traefik v3, released in 2024, dropped a lot of legacy baggage and tightened the middleware API.

The trade-off is real: Traefik's config syntax has a learning curve. Plan 30-60 minutes the first time.

Hardware This Setup Runs On

I run this on a Beelink EQ12 mini PC as the Proxmox host β€” 16GB RAM, N100, pulls 6W idle. Plenty of headroom for a full home lab stack. Storage is a Samsung 870 EVO 1TB SATA SSD for the OS drive and a WD Red Plus 4TB NAS drive for bulk storage. Networking runs through a TP-Link TL-SG108E 8-port managed switch with static DHCP leases for every node.

If you're expanding to something bigger, the Minisforum MS-01 with dual 10GbE is worth the investment, and you'll want a CyberPower CP1500PFCLCD UPS to keep everything online during brief outages.

Step 1: Create the Traefik LXC

Use the community script β€” it's maintained, tested, and saves 20 minutes:

bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/traefik.sh)"

Accept defaults: Debian 12, 512MB RAM, 1 vCPU, 4GB disk. Assign it a static IP β€” I use 192.168.0.10 and reserve it at the router level. After creation, note the container ID. Set the hostname:

bash
pct exec <CTID> -- hostnamectl set-hostname traefik

Step 2: Static Configuration (traefik.yaml)

The static config lives at /etc/traefik/traefik.yaml inside the LXC. This is Traefik's startup config β€” it does not hot-reload. SSH into the LXC:

bash
pct enter <CTID>
nano /etc/traefik/traefik.yaml

Here's a production-ready static config:

yaml
global:
  checkNewVersion: false
  sendAnonymousUsage: false

api: dashboard: true insecure: false

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

accessLog: filePath: /var/log/traefik/access.log

entryPoints: web: address: ":80" http: redirections: entryPoint: to: websecure scheme: https websecure: address: ":443" http: tls: certResolver: letsencrypt

certificatesResolvers: letsencrypt: acme: email: your@email.com storage: /etc/traefik/acme.json dnsChallenge: provider: cloudflare resolvers: - "1.1.1.1:53" - "8.8.8.8:53"

providers: file: directory: /etc/traefik/conf.d watch: true docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false network: traefik_net

Key decisions: HTTP auto-redirects to HTTPS at the entrypoint level, exposedByDefault: false means you opt-in containers instead of accidentally exposing everything, and the DNS-01 challenge means you don't need port 80 open externally β€” critical for home labs behind CGNAT.

Step 3: Cloudflare API Token for DNS-01

In Cloudflare: Profile β†’ API Tokens β†’ Create Token β†’ "Edit zone DNS" template. Scope it to your specific zone. Copy the token.

Set it as an environment variable in Traefik's systemd unit:

bash
systemctl edit traefik

Add:

ini
[Service]
Environment="CF_DNS_API_TOKEN=your_cloudflare_token_here"

Create and lock down the ACME storage file:

bash
touch /etc/traefik/acme.json
chmod 600 /etc/traefik/acme.json

Restart Traefik:

bash
systemctl restart traefik
journalctl -u traefik -f

First run, test against the Let's Encrypt staging CA by adding caServer: "https://acme-staging-v02.api.letsencrypt.org/directory" to the acme: block. Do this before going live or you'll hit rate limits.

Step 4: File Provider β€” Routing Non-Docker Services

The file provider watches /etc/traefik/conf.d/ and hot-reloads on change. Create one YAML per service or group them. Here's Proxmox itself on 192.168.0.100:8006:

yaml
# /etc/traefik/conf.d/proxmox.yaml
http:
  routers:
    proxmox:
      rule: "Host(proxmox.yourdomain.com)"
      service: proxmox
      tls:
        certResolver: letsencrypt
      middlewares:
        - secure-headers

services: proxmox: loadBalancer: servers: - url: "https://192.168.0.100:8006" passHostHeader: true serversTransport: ignorecert

serversTransports: ignorecert: insecureSkipVerify: true

middlewares: secure-headers: headers: stsSeconds: 31536000 stsIncludeSubdomains: true contentTypeNosniff: true frameDeny: true browserXssFilter: true

insecureSkipVerify is necessary for Proxmox's self-signed cert β€” Traefik terminates TLS externally, so this is safe.

Step 5: Docker Provider β€” Automatic Container Routing

For Docker-based services, use labels in your docker-compose.yml. Here's Immich:

yaml
services:
  immich-server:
    image: ghcr.io/immich-app/immich-server:release
    networks:
      - traefik_net
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.immich.rule=Host(photos.yourdomain.com)"
      - "traefik.http.routers.immich.entrypoints=websecure"
      - "traefik.http.routers.immich.tls.certresolver=letsencrypt"
      - "traefik.http.services.immich.loadbalancer.server.port=2283"

networks: traefik_net: external: true

Create the shared network first:

bash
docker network create traefik_net

Traefik sees the label, creates the router and service, and requests a cert β€” all without a restart. This is why engineers prefer Traefik over GUI-based alternatives.

Step 6: Securing the Dashboard

Don't run the dashboard with insecure: true. Add HTTP Basic Auth:

bash
htpasswd -nb admin yourpassword

Paste the output into a file provider config:

yaml
# /etc/traefik/conf.d/dashboard.yaml
http:
  routers:
    dashboard:
      rule: "Host(traefik.yourdomain.com) && (PathPrefix(/api) || PathPrefix(/dashboard))"
      service: api@internal
      tls:
        certResolver: letsencrypt
      middlewares:
        - dashboard-auth

middlewares: dashboard-auth: basicAuth: users: - "admin:$apr1$..." # output from htpasswd

Firewall and Port Forwarding

Open ports 80 and 443 on your router pointing to the Traefik LXC's static IP. That's the only external exposure. Everything else stays internal. If you've configured OPNsense as your Proxmox firewall, create a NAT rule there instead of relying on your ISP router β€” it gives you per-rule logging and easier VLAN segmentation.

If you're on CGNAT or prefer not to forward any ports, Traefik pairs cleanly with Cloudflare Tunnels: Traefik handles internal routing and TLS, the tunnel handles external access without port 443 open.

Common Gotchas

Certificate not issuing. 99% of the time it's acme.json permissions or a Cloudflare token scope mismatch. Check journalctl -u traefik -f immediately after restart β€” the error message is explicit.

502 Bad Gateway. Traefik reached the router but the backend is down or the port is wrong. Verify with curl http://192.168.0.x:PORT from inside the Traefik LXC.

Router not matching. Rules are case-sensitive. Host() requires a lowercase hostname β€” Host('Photos.domain.com') won't match a request for photos.domain.com.

Docker socket security. Mounting /var/run/docker.sock directly gives Traefik root-equivalent Docker daemon access. In a shared or semi-trusted environment, front it with docker-socket-proxy and limit to read-only API calls.

How This Fits Into a Broader Stack

Traefik is the ingress layer β€” it doesn't replace a container manager. If you're running Coolify v4 on Proxmox LXC, configure Coolify to use Traefik as its proxy instead of the built-in Caddy. The two cooperate well: Coolify handles deployment lifecycle, Traefik owns routing and cert management.

For a dedicated always-on Traefik node separate from your main Proxmox host, a Raspberry Pi 5 8GB draws under 5W idle. Pair it with a USB SSD enclosure and a 120GB SSD for better write endurance than SD card.

Verdict

Traefik v3 is the right reverse proxy for a home lab growing past toy status. The learning curve is real but bounded β€” once static config and the first file provider route are working, each new service takes five minutes to add. The automatic Docker label routing is genuinely better than any GUI-based alternative, and the Let's Encrypt + Cloudflare DNS-01 flow delivers real certs even behind CGNAT.

The community-scripts helper gets you from zero to running in under 10 minutes. This guide covers the 90% of config that the script leaves to you.

---

Disclosure: This post contains affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you.

πŸ“¬Weekly Newsletter

Get the best home lab & AI content

No spam. One email per week. Unsubscribe anytime.

Share this article