Files
2026-08-27 12:07:31 +03:30

135 lines
6.7 KiB
Markdown

# Traefik
Self-hosted reverse proxy replacing Nginx Proxy Manager — gives every service a friendly `<service>.home.server` hostname with locally-trusted HTTPS, configured entirely as code.
## Why the migration from Nginx Proxy Manager
NPM works fine, but the switching cost grew as the homelab did. Two things pushed the move:
### 1. Infrastructure as code
NPM's entire configuration lives behind a web UI — proxy hosts, SSL certificates, custom locations, access rules. None of it is version-controlled, exportable, or reproducible. If the container dies and comes back without its data volume, you're rebuilding every proxy host by hand.
Traefik is the opposite: every route, middleware, TLS cert, and entrypoint is defined in YAML files that live in this repo. The compose file, the `dynamic/` configs, the cert files — all version-controlled, all diffable, all reproducible with `docker compose up -d`. This is the same pattern the rest of the homelab already follows (each service is a Compose file in a folder), and Traefik fits that model naturally where NPM didn't.
### 2. Docker daemon auto-discovery
NPM requires you to manually add each proxy host through its web UI — type the domain, point it at the container's IP and port, attach an SSL cert, save. Every new service means another round-trip through the UI.
Traefik talks directly to the Docker daemon and watches for containers with `traefik.enable=true`. Add a few labels to any compose file and Traefik picks up the new route automatically — no UI interaction, no restart, no manual configuration. This means adding a new service to the reverse proxy is a two-line addition to that service's compose file, not a separate step in a different tool.
## How it works
```
traefik/
├── compose.yaml # Traefik container + static config
├── certs/ # Local TLS certificates (mkcert)
│ ├── home.server.crt
│ └── home.server.key
└── dynamic/ # File-based provider — routes, middlewares, TLS
├── tls.yaml # Default TLS cert store
├── middlewares.yaml # HTTPS redirect + security headers
├── pihole.yaml # Pi-hole route
├── plex.yml # Plex route (external host)
├── sentinel.yml # Sentinel route (external host)
└── dockscope-auth.yml # Basic auth middleware for DockScope
```
### Static config (`compose.yaml`)
Traefik's static configuration is passed as command-line flags:
- **Docker provider** — watches the Docker daemon for labeled containers on the `web_net` network
- **File provider** — watches `/dynamic/` for YAML config changes (hot-reloaded, no restart needed)
- **Entrypoints** — `:80` (HTTP) and `:443` (HTTPS)
- **API dashboard** — enabled but not insecure (accessed via its own HTTPS route)
### Dynamic config (`dynamic/`)
Routes and middlewares are defined as YAML files, not labels. This keeps the compose files clean and makes it easy to see all routing rules in one place:
- **`middlewares.yaml`** — shared `redirect-to-https` and `security-headers` middlewares (HSTS, XSS filter, content-type nosniff, SAMEORIGIN framing)
- **`tls.yaml`** — default TLS cert store pointing at the mkcert-generated certificate
- **`pihole.yaml`**, **`plex.yml`**, **`sentinel.yml`** — per-service route definitions for services that can't use Docker labels (host-network or external hosts)
### Services using Docker labels
Most services define their Traefik routes directly in their compose files via labels:
```yaml
labels:
- "traefik.enable=true"
- "traefik.docker.network=web_net"
- "traefik.http.routers.<service>.rule=Host(`<service>.home.server`)"
- "traefik.http.routers.<service>.entrypoints=websecure"
- "traefik.http.routers.<service>.tls=true"
- "traefik.http.routers.<service>.middlewares=security-headers@file"
- "traefik.http.routers.<service>-http.rule=Host(`<service>.home.server`)"
- "traefik.http.routers.<service>-http.entrypoints=web"
- "traefik.http.routers.<service>-http.middlewares=redirect-to-https@file"
- "traefik.http.services.<service>.loadbalancer.server.port=<internal-port>"
```
Services currently fronted by Traefik:
| Service | Hostname | Internal Port |
|---|---|---|
| Prometheus | `prometheus.home.server` | 9090 |
| Grafana | `grafana.home.server` | 3000 |
| cAdvisor | `cadvisor.home.server` | 8080 |
| Loki | `loki.home.server` | 3100 |
| Alloy | `alloy.home.server` | 12345 |
| Pi-hole | `pihole.home.server` | 8082 (host) |
| Portainer | `portainer.home.server` | 9000 |
| n8n | `n8n.home.server` | 5678 |
| Filebrowser | `filebrowser.home.server` | 80 |
| DockScope | `dockscope.home.server` | 4681 |
| Semaphore | `semaphore.home.server` | 3000 |
| MinIO | `minio.home.server` | 9001 |
| Plex | `plex.home.server` | 32400 (external) |
| Sentinel | `sentinel.home.server` | 8088 (external) |
| Vaultwarden | `vaultwarden.home.server` | 80 |
| Traefik Dashboard | `traefik.home.server` | API@internal |
## TLS certificates
Same approach as before — [mkcert](https://github.com/FiloSottile/mkcert) generates a local CA and signs certificates for `*.home.server`. The cert and key live in `certs/` and are mounted read-only into the Traefik container.
Traefik loads the default cert via `dynamic/tls.yaml`:
```yaml
tls:
stores:
default:
defaultCertificate:
certFile: /certs/home.server.crt
keyFile: /certs/home.server.key
```
Adding a new service with a `.home.server` hostname automatically gets the right cert — no per-host certificate assignment needed.
## Adding a new service
1. **If the service is a Docker container on `web_net`** — add Traefik labels to its compose file (copy the pattern from any existing service above)
2. **If the service is host-network or external** — create a new YAML file in `dynamic/` with the router and service definition
3. **Add the hostname to Pi-hole** — so DNS resolves `<service>.home.server` to the server's LAN IP
No restart required — Traefik watches both the Docker daemon and the `dynamic/` directory and picks up changes automatically.
## Network
All Traefik-fronted services join the `web_net` external Docker network. This network must be created before starting Traefik:
```bash
docker network create web_net
```
## Security notes
- The Traefik dashboard is behind its own HTTPS route with security headers — not exposed on the public internet, LAN-only via Pi-hole DNS
- DockScope additionally uses a basicAuth middleware (`dynamic/dockscope-auth.yml`) since it mounts the Docker socket
- All HTTP requests are redirected to HTTPS permanently (301)
- Security headers include HSTS (1 year, includeSubdomains, preload), XSS filter, content-type nosniff, and SAMEORIGIN framing
[← Back to Home](../../docs/Home.md)