fix: format compliance, v3.7, expanded migration, serversTransport ref, templates, healthcheck

Phase 1 — Format compliance:
- Add version (0.1.0) and compatibility fields to frontmatter
- Tighten description to trigger-first style (~174 chars)
- Fix 'When NOT to Use' contradiction on plugins
- Remove non-standard spec-version from metadata

Phase 2 — Content enrichment:
- Bump all image references from v3.2 to v3.7 across 7 files
- Add pre-migration audit checklist (10 items) to migration reference
- Add tracing/provider removal detail to migration reference
- Create servers-transport.md (191 lines) extracted from
  static-configuration.md with expanded mTLS, SPIFFE, CRD coverage

Phase 3 — Templates & scripts:
- Add templates/docker-compose.yml — production compose with socket proxy,
  Let's Encrypt, dashboard auth, HTTP/3, security hardening
- Add scripts/traefik-healthcheck.sh — agent-compatible health check with
  --json output, checks ping, API, router count, certificate expiry

Signed-off-by: Jasper <magnus@groktop.us>
This commit is contained in:
Magnus Hedemark
2026-07-05 16:12:35 -04:00
parent 13801a38a4
commit 41646af1b9
11 changed files with 504 additions and 26 deletions
+19 -7
View File
@@ -1,14 +1,16 @@
---
name: traefik
description: >-
Deploy, configure, secure, and maintain Traefik v3 reverse proxy — Docker
provider, HTTP/TCP/UDP routing, TLS/ACME (Let's Encrypt), middlewares,
observability, API, and production deployment. Load when configuring or
troubleshooting a Traefik instance.
Deploy, configure, and troubleshoot Traefik v3 reverse proxy — covers all
providers, routing, TLS/ACME, middlewares, and production patterns with
YAML examples. Load when setting up or debugging a Traefik instance.
license: MIT
version: 0.1.0
compatibility: >-
Compatible with any agent supporting the Agent Skills format (Hermes Agent,
Claude Code, GitHub Copilot, OpenCode, Cursor, etc.)
metadata:
source: https://doc.traefik.io/traefik/
spec-version: "1.0"
---
# Traefik Agent Skill
@@ -17,11 +19,20 @@ Comprehensive reference for deploying, configuring, and maintaining **Traefik v3
## Quick Start — Minimal Docker Deployment
A production-ready Docker Compose template is available at `templates/docker-compose.yml`. For a quick test:
### One-Line Health Check
```bash
bash scripts/traefik-healthcheck.sh # Text output
bash scripts/traefik-healthcheck.sh --json # JSON output for agents
```
```yaml
# docker-compose.yml
services:
traefik:
image: traefik:v3.2
image: traefik:v3.7
command:
# Static configuration via CLI args
- "--providers.docker=true"
@@ -66,6 +77,7 @@ The request flow: `EntryPoint → Router → (Middlewares) → Service → Backe
| **Observability** | Prometheus/OTel metrics, access logs, tracing, health checks | `references/observability.md` |
| **v2→v3 Migration** | Breaking changes, rule syntax update, deprecated options | `references/migration-v2-to-v3.md` |
| **Production Patterns** | Docker Compose template, security hardening, HA, monitoring | `references/production-deployment.md` |
| **Servers Transport** | Backend connection config, mTLS to backends, connection pooling, SPIFFE | `references/servers-transport.md` |
| **Kubernetes Providers** | Deploying Traefik in K8s — Ingress, CRD (IngressRoute), Gateway API | `references/kubernetes-providers.md` |
| **Other Providers** | ECS, Nomad, Consul Catalog, KV stores, File, HTTP, REST providers | `references/other-providers.md` |
| **Community Patterns** | Production wisdom — middleware ordering, performance tuning, CDN real-IP, CrowdSec, Authelia, troubleshooting | `references/community-patterns.md` |
@@ -88,4 +100,4 @@ The request flow: `EntryPoint → Router → (Middlewares) → Service → Backe
## When NOT to Use This Skill
- For Traefik Hub, Traefik Enterprise, or Traefik Mesh — these are separate products with different APIs
- For writing Traefik plugins — see the official plugin development documentation at https://plugins.traefik.io/create
- For developing Traefik plugins (Yaegi or WASM) — this skill covers *using* configured plugins, not writing them. See https://plugins.traefik.io/create for plugin development.
+1 -1
View File
@@ -46,7 +46,7 @@ Or with Docker labels:
```yaml
services:
traefik:
image: traefik:v3.2
image: traefik:v3.7
labels:
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
+1 -1
View File
@@ -370,7 +370,7 @@ Request → Traefik → CrowdSec Bouncer (ForwardAuth) → Backend Service
```yaml
services:
traefik:
image: traefik:v3.2
image: traefik:v3.7
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./logs:/var/log/traefik
+1 -1
View File
@@ -20,7 +20,7 @@ providers:
```yaml
services:
traefik:
image: traefik:v3.2
image: traefik:v3.7
volumes:
- /var/run/docker.sock:/var/run/docker.sock # Required
- ./traefik.yml:/etc/traefik/traefik.yml # Static config
+78 -13
View File
@@ -11,7 +11,27 @@ This covers the key changes when migrating from Traefik v2 to v3. Full details:
| `Path` matcher wildcards | Supported | Removed (use `PathRegexp` or `PathPrefix`) |
| `insecure` field renamed | N/A | `tls.insecureSkipVerify` on serversTransport |
| X-Forwarded-For behavior | Always appended | Configurable via `forwardedHeaders` |
| Deprecated field removals | Various | Strictly enforced removal |
| Tracing providers | Jaeger, Zipkin, Datadog, Elastic | OpenTelemetry (OTLP) only |
| Content-Type handling | Auto-detected | Must use `ContentType` middleware |
| IPWhiteList renamed | `IPWhiteList` | `IPAllowList` |
| Provider removals | Marathon, Rancher v1, InfluxDB v1, Pilot | Removed entirely |
| `tls.caOptional` | Available | Removed from all providers |
| Deprecated field removals | Various | Strictly enforced — v3 refuses to start with deprecated fields |
## Pre-Migration Audit Checklist
Before upgrading, run through these checks:
- [ ] **Rule syntax audit** — Search config files for `HostRegexp` with wildcard patterns (`.+\.`); replace with `Host(`*.`)`
- [ ] **Path wildcard audit** — Search for curly-brace path patterns like `/api/{version:...}`; these need `PathRegexp` replacements
- [ ] **TLS compatibility check** — Verify clients support TLS 1.2+ (default changed from TLS 1.0)
- [ ] **ServersTransport rename** — Replace all `insecure: true` with `insecureSkipVerify: true`
- [ ] **Tracing migration** — If using Jaeger/Zipkin/Datadog/Elastic APM, migrate to OpenTelemetry
- [ ] **ContentType audit** — If you relied on Traefik auto-detecting Content-Type, add the `ContentType` middleware
- [ ] **IPWhiteList → IPAllowList** — Rename any `ipWhiteList` config blocks to `ipAllowList`
- [ ] **Remove deprecated providers** — If using Marathon, Rancher, InfluxDB v1 metrics, or Pilot, find alternatives
- [ ] **Default certificate** — If you relied on SNI fallback, configure a default certificate explicitly
- [ ] **Test in staging** — Run a v3 instance alongside v2 with `core.defaultRuleSyntax: v2` for gradual migration
## Rule Syntax Changes
@@ -21,10 +41,10 @@ The v3 rule syntax is the default. If you have v2 rules, they'll get a deprecati
```yaml
# v2 — required HostRegexp for wildcards
rule: "HostRegexp(`.+\.example\.com`)"
rule: "HostRegexp(`.+\\.example\\.com`)"
# v3 — native wildcard support (preferred)
rule: "Host(`*.example.com`)"
rule: "Host(`*.example.com`)" # matches foo.example.com, NOT foo.bar.example.com
```
### Path Wildcards Removed
@@ -45,7 +65,7 @@ To use v2 syntax during migration, set per-router:
http:
routers:
legacy-router:
rule: "HostRegexp(`.+\.example\.com`)"
rule: "HostRegexp(`.+\\.example\\.com`)"
ruleSyntax: "v2" # Per-router override
```
@@ -67,9 +87,9 @@ tls:
minVersion: VersionTLS12
```
## TLS Certificate Fallback
### TLS Certificate Fallback
In v3, when no TLS certificate matches the requested SNI, Traefik v3 returns a certificate error instead of falling back to a default certificate. Configure a default certificate explicitly:
In v3, when no TLS certificate matches the requested SNI, Traefik returns a certificate error instead of falling back to a default certificate. Configure a default certificate explicitly:
```yaml
tls:
@@ -80,6 +100,37 @@ tls:
keyFile: "/certs/default-key.pem"
```
## Tracing Changes (OTLP Only)
v3 removed direct support for Jaeger, Zipkin, Datadog APM, and Elastic APM tracing. All tracing now goes through OpenTelemetry (OTLP):
```yaml
# v3 — OTLP only
tracing:
serviceName: "traefik"
sampleRate: 1.0
otlp:
grpc:
endpoint: "localhost:4317"
insecure: true
http:
endpoint: "localhost:4318"
```
Migrate from Jaeger/Zipkin agents by running an OpenTelemetry Collector as a sidecar that forwards to your existing backend.
## Content-Type Handling
v3 removed automatic Content-Type detection. Responses without an explicit Content-Type header may be handled differently:
```yaml
# v3 — add this middleware to preserve v2 behavior
http:
middlewares:
auto-content-type:
contentType: {}
```
## ServersTransport Changes
The `insecure` field has been renamed for clarity:
@@ -120,12 +171,26 @@ entryPoints:
# When set to true, Traefik will NOT append client's RemoteAddr to X-Forwarded-For
```
## Provider Removals
The following providers were removed in v3 with no replacement:
| Removed Provider | Alternative |
|-----------------|-------------|
| Marathon | Use File provider or migrate to Kubernetes/Nomad |
| Rancher v1 | Use File provider or Rancher v2's Kubernetes ingress |
| InfluxDB v1 metrics | Use InfluxDB v2 or Prometheus |
| Pilot | Use Traefik Hub or forward auth middlewares |
## Migration Steps
1. **Update static config** — review all entryPoints, providers, and TLS settings
2. **Update rule syntax** — replace `HostRegexp` wildcards with `Host(*)`, remove wildcards from `Path` matchers
3. **Check TLS defaults** — verify min TLS version compatibility with clients
4. **Update ServersTransport** — rename `insecure` to `insecureSkipVerify`
5. **Test with staging** — use `core.defaultRuleSyntax: v2` during migration and test each router
6. **Add default certificate** — if you relied on the TLS fallback behavior
7. **Test** — run a staging instance alongside v2, verify all routes work
1. **Run the pre-migration audit** — check all 10 items above before changing anything
2. **Update static config** — review all entryPoints, providers, and TLS settings
3. **Update rule syntax** — replace `HostRegexp` wildcards with `Host(*)`, remove wildcards from `Path` matchers
4. **Check TLS defaults** — verify min TLS version compatibility with clients
5. **Update ServersTransport** — rename `insecure` to `insecureSkipVerify`
6. **Migrate tracing** — switch from direct Jaeger/Zipkin to OpenTelemetry
7. **Test with staging** — use `core.defaultRuleSyntax: v2` during migration and test each router
8. **Add default certificate** — if you relied on the TLS fallback behavior
9. **Run a parallel stack** — deploy v3 alongside v2, redirect a subset of traffic, verify all routes work
10. **Cut over** — switch production traffic to v3, keep v2 as rollback target for 48 hours
+2 -2
View File
@@ -9,7 +9,7 @@ version: "3.8"
services:
traefik:
image: traefik:v3.2
image: traefik:v3.7
restart: unless-stopped
security_opt:
- no-new-privileges:true
@@ -113,7 +113,7 @@ services:
- TASKS=1
traefik:
image: traefik:v3.2
image: traefik:v3.7
environment:
- DOCKER_HOST=tcp://docker-proxy:2375
# Do NOT mount the docker socket directly
+191
View File
@@ -0,0 +1,191 @@
# ServersTransport Reference
Defines how Traefik connects to backend servers. Configured in static config and referenced per-service in dynamic config.
## HTTP ServersTransport
Controls the connection between Traefik and HTTP backend services.
```yaml
# Static config
serversTransport:
insecureSkipVerify: false # Skip TLS verification to backend
rootCAs: # List of root CA certificate paths
- /etc/traefik/certs/ca.pem
maxIdleConnsPerHost: 200 # Max idle connections per backend host
forwardingTimeouts:
dialTimeout: 30s # TCP dial timeout to backend
responseHeaderTimeout: 0s # Timeout for backend response headers (0=unlimited)
idleConnTimeout: 90s # Idle keep-alive connection timeout
spiffe:
ids: [] # SPIFFE IDs for workload identity
trustDomain: "" # SPIFFE trust domain
```
### Configuration Options
| Option | Description | Default |
|--------|-------------|---------|
| `insecureSkipVerify` | Skip TLS certificate verification for backend connections | `false` |
| `rootCAs` | Paths to PEM-encoded CA certificates for backend TLS verification | `[]` |
| `maxIdleConnsPerHost` | Maximum idle connections kept per backend host | `200` |
| `forwardingTimeouts.dialTimeout` | Maximum time to establish TCP connection to backend | `30s` |
| `forwardingTimeouts.responseHeaderTimeout` | Maximum time to wait for response headers from backend | `0s` (no timeout) |
| `forwardingTimeouts.idleConnTimeout` | Maximum time a keep-alive connection can remain idle | `90s` |
| `spiffe.ids` | Allowed SPIFFE identities for workload identity | `[]` |
| `spiffe.trustDomain` | SPIFFE trust domain for workload identity | `""` |
## TCP ServersTransport
Controls connections for TCP routing (non-HTTP backends).
```yaml
# Static config
tcpServersTransport:
dialKeepAlive: 30s # Keep-alive probe interval for TCP connections
dialTimeout: 30s # TCP dial timeout
terminationDelay: 0s # Delay before terminating TCP connection on shutdown
tls:
insecureSkipVerify: false # Skip TLS verification
rootCAs: # Root CA certificates for backend TLS
- /etc/traefik/certs/ca.pem
spiffe:
ids: []
trustDomain: ""
```
### Configuration Options
| Option | Description | Default |
|--------|-------------|---------|
| `dialKeepAlive` | TCP keep-alive probe interval | `30s` |
| `dialTimeout` | TCP dial timeout | `30s` |
| `terminationDelay` | Delay before TCP connection termination on shutdown | `0s` |
| `tls.insecureSkipVerify` | Skip TLS verification for backend | `false` |
| `tls.rootCAs` | Root CA certificate paths | `[]` |
| `tls.spiffe.ids` | Allowed SPIFFE identities | `[]` |
| `tls.spiffe.trustDomain` | SPIFFE trust domain | `""` |
## Per-Service ServersTransport Override
Reference a named serversTransport from a specific service in dynamic config:
```yaml
# Dynamic config (File provider)
http:
serversTransports:
mTLS-to-backend:
insecureSkipVerify: false
rootCAs:
- /etc/traefik/certs/internal-ca.pem
forwardingTimeouts:
dialTimeout: 10s
services:
secure-api:
loadBalancer:
servers:
- url: "https://backend.internal:443"
serversTransport: "mTLS-to-backend@file"
```
Via Docker labels:
```yaml
labels:
- "traefik.http.services.api.loadbalancer.serverstransport=mTLS-to-backend@file"
```
## mTLS Between Traefik and Backends
For mutual TLS (backends that require client certificates):
```yaml
# Dynamic config
http:
serversTransports:
mtls:
insecureSkipVerify: false
rootCAs:
- /etc/traefik/certs/ca.pem # CA that signed the backend cert
# NOTE: serversTransport does NOT support client cert/key fields.
# For client certificate authentication to backends, configure
# TLS at the service level or use ForwardAuth.
services:
internal-api:
loadBalancer:
serversTransport: "mtls@file"
servers:
- url: "https://api.internal:8443"
```
## Kubernetes CRD Reference
In Kubernetes, define a `ServersTransport` CRD:
```yaml
apiVersion: traefik.io/v1alpha1
kind: ServersTransport
metadata:
name: mtls-transport
spec:
serverName: "api.internal"
insecureSkipVerify: false
rootCAs:
- secret: internal-ca-secret # Kubernetes Secret reference
forwardingTimeouts:
dialTimeout: 30s
responseHeaderTimeout: 30s
idleConnTimeout: 90s
```
Reference in an IngressRoute:
```yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
spec:
routes:
- kind: Rule
match: Host(`app.example.com`)
services:
- name: api-service
port: 8443
serversTransport: mtls-transport
```
For TCP services, use `ServersTransportTCP`:
```yaml
apiVersion: traefik.io/v1alpha1
kind: ServersTransportTCP
metadata:
name: tcp-mtls
spec:
tls:
insecureSkipVerify: false
serverName: "db.internal"
rootCAs:
- secret: db-ca-secret
```
## Connection Pooling Best Practices
```yaml
# For high-throughput APIs — minimize connection churn
serversTransport:
maxIdleConnsPerHost: 500
forwardingTimeouts:
dialTimeout: 5s # Fast fail on unavailable backends
responseHeaderTimeout: 30s
idleConnTimeout: 120s # Longer idle time reduces reconnects
# For latency-sensitive services — tight timeouts
serversTransport:
maxIdleConnsPerHost: 50
forwardingTimeouts:
dialTimeout: 3s
responseHeaderTimeout: 10s
idleConnTimeout: 30s
```
@@ -33,6 +33,10 @@ spiffe:
workloadAPIAddr: "" # SPIRE agent socket path
## Server Transport (HTTP)
See `references/servers-transport.md` for full detail on serversTransport, tcpServersTransport, connection pooling, backend TLS, and per-service transport overrides.
```yaml
serversTransport:
insecureSkipVerify: false # Skip TLS verification to backend
rootCAs: # List of root CA certificates
@@ -47,6 +51,8 @@ serversTransport:
trustDomain: ""
## TCP Server Transport
```yaml
tcpServersTransport:
dialKeepAlive: 30s
dialTimeout: 30s
@@ -57,6 +63,7 @@ tcpServersTransport:
spiffe:
ids: []
trustDomain: ""
```
## EntryPoints — Network listeners
entryPoints:
+1 -1
View File
@@ -172,7 +172,7 @@ labels:
# docker-compose.yml
services:
traefik:
image: traefik:v3.2
image: traefik:v3.7
command:
- "--entrypoints.postgres.address=:5432"
- "--providers.docker=true"
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env bash
# traefik-healthcheck.sh — Check a running Traefik instance health
# Usage: ./traefik-healthcheck.sh [--json] [--url https://traefik.example.com]
#
# Requires: curl, jq
# Non-interactive, agent-friendly.
set -euo pipefail
# Defaults
URL="${TRAEFIK_URL:-http://localhost:8080}"
JSON=false
EXIT_CODE=0
while [[ $# -gt 0 ]]; do
case "$1" in
--json) JSON=true; shift ;;
--url) URL="$2"; shift 2 ;;
--help|-h)
echo "Usage: traefik-healthcheck.sh [--json] [--url <traefik-url>]"
echo ""
echo "Checks ping, API, version, router counts, and certificate expiry."
echo "Defaults to http://localhost:8080. Set TRAEFIK_URL env var to override."
exit 0
;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
json_out() {
if $JSON; then
echo "$1"
fi
}
text_out() {
if ! $JSON; then
echo "$1"
fi
}
# 1. Ping check
ping_status=$(curl -s -o /dev/null -w "%{http_code}" "${URL}/ping" 2>/dev/null || echo "000")
if [ "$ping_status" = "200" ]; then
text_out "✓ Ping: OK (200)"
json_out "{\"ping\": {\"status\": \"ok\", \"code\": 200}"
else
text_out "✗ Ping: FAILED ($ping_status)"
json_out "{\"ping\": {\"status\": \"fail\", \"code\": $ping_status}"
EXIT_CODE=1
fi
# 2. API health check
api_status=$(curl -s -o /dev/null -w "%{http_code}" "${URL}/api/version" 2>/dev/null || echo "000")
if [ "$api_status" = "200" ]; then
version=$(curl -s "${URL}/api/version" 2>/dev/null | jq -r '.Version // "unknown"' 2>/dev/null || echo "unknown")
text_out "✓ API: OK ($api_status) — Traefik $version"
json_out ", \"api\": {\"status\": \"ok\", \"version\": \"$version\"}"
else
text_out "✗ API: FAILED ($api_status) — is the API enabled?"
json_out ", \"api\": {\"status\": \"fail\", \"code\": $api_status}"
EXIT_CODE=1
fi
# 3. Router count
routers=$(curl -s "${URL}/api/http/routers" 2>/dev/null | jq length 2>/dev/null || echo "N/A")
text_out " HTTP routers: $routers"
json_out ", \"routers\": {\"count\": $routers}"
tcp_routers=$(curl -s "${URL}/api/tcp/routers" 2>/dev/null | jq length 2>/dev/null || echo "N/A")
text_out " TCP routers: $tcp_routers"
json_out ", \"tcp_routers\": {\"count\": $tcp_routers}"
# 4. Overview
overview=$(curl -s "${URL}/api/overview" 2>/dev/null || echo "{}")
if [ "$overview" != "{}" ]; then
total=$(echo "$overview" | jq -r '.http.routers.total // 0' 2>/dev/null)
text_out " Total HTTP routers (overview): $total"
fi
# 5. Certificate expiry check
certs=$(curl -s "${URL}/api/rawconfig" 2>/dev/null | jq -r '.tls.certificates // []' 2>/dev/null || echo "[]")
if [ "$certs" != "[]" ]; then
cert_count=$(echo "$certs" | jq length)
text_out " TLS certificates: $cert_count"
json_out ", \"tls_certificates\": {\"count\": $cert_count}"
else
json_out ", \"tls_certificates\": null"
fi
# Close JSON
if $JSON; then
echo "}"
fi
exit $EXIT_CODE
+107
View File
@@ -0,0 +1,107 @@
# Production Traefik Deployment — Docker Compose
# Target: Traefik v3.7+
# Usage: docker compose up -d
#
# Features:
# - Docker provider (read-only socket via docker-socket-proxy)
# - Let's Encrypt ACME (HTTP-01 challenge)
# - Dashboard with BasicAuth (internal-only)
# - Security hardening (read-only rootfs, no-new-privileges, capability drop)
# - Prometheus metrics endpoint
# - JSON structured logging
# - HTTP/3 (QUIC) enabled
version: "3.8"
x-logging: &default-logging
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
# --- Docker Socket Proxy (Security) ---
# Replaces direct docker.sock mount with a read-only API proxy
docker-proxy:
image: tecnativa/docker-socket-proxy:latest
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- NETWORKS=1
- SERVICES=1
- TASKS=1
- INFO=1
networks:
- traefik
logging: *default-logging
# --- Traefik Reverse Proxy ---
traefik:
image: traefik:v3.7
restart: unless-stopped
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
networks:
- traefik
ports:
- "80:80"
- "443:443"
# UDP port for HTTP/3 (QUIC)
- "443:443/udp"
environment:
# DNS challenge credentials (example: Cloudflare)
# - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
- TZ=UTC
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./letsencrypt:/letsencrypt
# Do NOT mount docker.sock directly — use docker-proxy
command:
# Providers
- "--providers.docker=true"
- "--providers.docker.endpoint=tcp://docker-proxy:2375"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=traefik"
# File provider for shared middlewares
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
# EntryPoints
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http3=true"
# TLS
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
# API (dashboard served through a router, not directly)
- "--api.dashboard=true"
- "--api.insecure=false"
# Logging
- "--log.level=INFO"
- "--log.format=json"
- "--accesslog=true"
- "--accesslog.format=json"
- "--accesslog.addinternals=false"
labels:
# Dashboard router
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls=true"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=${DASHBOARD_AUTH:-admin:$$2y$$10$$...}"
logging: *default-logging
networks:
traefik:
name: traefik
driver: bridge