Auth layer (Authentik 2026.8.0): - compose: 3-service stack (authentik-postgres, authentik-server, authentik-worker), postgres health-gated, bind mounts under /home/dank/authentik - Caddyfile: auth.franksdank.cloud vhost; forward_auth on the no-auth vhosts (homepage, SearXNG, Hermes, Prometheus, RomM, Copyparty); outpost path proxied first in a route block - .env.example: AUTHENTIK_PG_PASS + AUTHENTIK_SECRET_KEY - deploy.sh: render only CADDY_BASICAUTH_HASH (the homepage per-user hashes are retired; grafana + pihole keep basicauth) - tests/authentik.bats (new) + updated homepage/caddyfile/pihole seams - docs: Authentik.md + per-service pages; Caddy.md + Homepage.md updated Homepage (naked domain): - Caddyfile: dankclaw.franksdank.cloud -> franksdank.cloud www.franksdank.cloud; dankclaw + dankserver redirect to it; bare-IP catch-all updated - addn-hosts: A records for the naked domain, www, and auth (all -> 143) - tests/pihole.bats: vhost_names handles multi-address blocks; TLD check allows the naked domain All 146 tests pass.
6.3 KiB
Authentik — the homelab's SSO / OIDC provider
Authentik is the single sign-on (SSO) and OpenID Connect (OIDC) provider for the homelab. It fronts the Caddy vhosts that do not carry their own application-level authentication. Apps that have their own login (Grafana, Gitea, OpenWebUI, Jellyfin, the *arr stack, Pi-hole) are not wrapped in forward_auth — they handle their own session.
- Version:
2026.8.0(pinned indankclaw/compose.yaml) - Host: dankclaw (
192.168.0.143) - Name:
auth.franksdank.cloud - Stack: 3 compose services —
authentik-postgres,authentik-server,authentik-worker(see Pi-hole for the LAN-DNS pattern)
Why Authentik
The comparison that chose it (against Authelia, Keycloak, Zitadel, Ory) is in
.docaudit/research/auth.md. In short: full OIDC with single-logout, a web GUI
for all configuration, a native Caddy forward_auth outpost, and a footprint
that fits on a 3-user homelab (~1–1.5 GB idle). Redis was removed in 2025.10,
so the stack is 3 services, not 4.
Architecture
graph LR
subgraph Browser
U[User]
end
subgraph Caddy [Caddy - auth gate]
FA[forward_auth]
end
subgraph AK [Authentik]
SRV[server :9000]
WKR[worker]
PG[(postgres)]
OUT[outpost /auth/caddy]
end
subgraph App [Target app]
A1[SearXNG / Hermes / Prometheus<br/>RomM / Copyparty / homepage]
end
U -->|1. GET /| FA
FA -->|2. GET /outpost.../auth/caddy| OUT
OUT -->|3. no session: 302 login| U
U -->|4. login at auth.franksdank.cloud| SRV
SRV --> PG
WKR --> PG
SRV -->|5. 2xx: session valid| FA
FA -->|6. proxy with headers| A1
- The browser hits a protected vhost (e.g.
search.franksdank.cloud). - Caddy
forward_authclones the request to the Authentik outpost (/outpost.goauthentik.io/auth/caddy). - With no valid session, the outpost returns a
302to the Authentik login page (auth.franksdank.cloud). - The user logs in at Authentik (email + password, TOTP/WebAuthn MFA, or an upstream provider).
- On the next request the outpost returns
2xx; Caddy copies theX-Authentik-*headers and proxies to the app.
The three compose services
| Service | Role | Image |
|---|---|---|
authentik-postgres |
PostgreSQL 16 (users, flows, sessions, events) | docker.io/library/postgres:16-alpine |
authentik-server |
Core + admin API + the embedded Caddy outpost (port 9000) | ghcr.io/goauthentik/server:2026.8.0 |
authentik-worker |
Background tasks (event processing, provider sync, flows) | ghcr.io/goauthentik/server:2026.8.0 |
- Postgres is the only stateful store. Sessions, caching, and WebSockets moved to PostgreSQL in 2025.10 (Redis removed).
- Server and worker run the same image; the
commandselects the role (server/worker). - The worker runs as
0:0(rootless: container root maps to the host user that owns the data dir). - Data persists under
/home/dank/authentik/(bind mounts, repo policy — no named volumes).
Caddy integration
The Caddyfile wraps the no-auth vhosts in a route block. The pattern (one
example; the others are identical):
search.franksdank.cloud {
route {
# 1. The outpost path is proxied to Authentik (login UI + session).
reverse_proxy /outpost.goauthentik.io/* authentik-server:9000
# 2. forward_auth checks the session before serving the app.
forward_auth authentik-server:9000 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name
trusted_proxies private_ranges
}
# 3. The app.
reverse_proxy 192.168.0.143:8081
}
}
Vhosts protected by forward_auth: the homepage (franksdank.cloud,
www.franksdank.cloud), SearXNG, Hermes, Prometheus, RomM, Copyparty. The
auth.franksdank.cloud vhost proxies to the server directly (no forward_auth —
it is the login).
Configuration (secrets)
Two secrets live in the host .env (git-ignored; placeholders in
.env.example):
AUTHENTIK_PG_PASS— the PostgreSQL password. Generate:openssl rand -base64 32.AUTHENTIK_SECRET_KEY— the server's signing key. Generate:openssl rand -base64 48.
First-time setup
./scripts/deploy.sh dankclaw— pulls, starts the 3 services (postgres health-gated), reloads Caddy.- Open
https://auth.franksdank.cloud— the initial setup wizard runs. It creates the superuser (email + password) and generates the DB schema. - In the admin interface: Directory → Applications → add a Proxy application (or an OAuth2/OpenID Connect provider) for each vhost you protect. The Caddy outpost reads the application's policy.
- For apps with native OIDC (Grafana, Gitea, OpenWebUI): add an OAuth2/OpenID
Connect provider, create the app with the correct redirect URI, and paste
the
client_id/client_secretinto the app's SSO settings.
Operations
- Health:
https://auth.franksdank.cloud/-/health/(the server healthcheck probes this). - Logs:
podman logs authentik-server/podman logs authentik-worker. - Backups:
pg_dumptheauthentik-postgresdatabase (the single source of truth). The/home/dank/authentik/dir holds media + static files. - Upgrades: bump the image tag in
dankclaw/compose.yaml, run./scripts/deploy.sh dankclaw. Authentik migrates the schema on startup.
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
| Every vhost 502s at the outpost | authentik-server down |
podman logs authentik-server; check postgres health |
| Login loops (302 → login → 302) | trusted_proxies / X-Forwarded-* mismatch |
Confirm Caddy sets X-Forwarded-For; the outpost trusts private_ranges |
| "Invalid signature" on the outpost | AUTHENTIK_SECRET_KEY differs between server and worker |
Both services read the same .env var; re-deploy |
| Postgres "password authentication failed" | AUTHENTIK_PG_PASS changed after first init |
Reset via pg_ctl or re-init the data dir |
Cross-references
- Caddy — the reverse proxy that carries the forward_auth directives
- Pi-hole — the LAN DNS that resolves
auth.franksdank.cloud - Network Architecture — the flat naming scheme
- Research:
.docaudit/research/auth.md— the full provider comparison