Skip to content

Services

Everything below runs on Lana via Docker Compose, defined in docker-infra — one directory per service. See Networking for what “Access (self-hosted)” / “Access (SSO)” actually mean.

Service Hostname Role Auth
Gitea git.spelvin.dev Git host — docker-infra itself lives here (legacy OpenID 2.0 URL-based sign-in/sign-up now disabled — see below) Access (SSO)
LiteLLM litellm.spelvin.dev LLM proxy — home Ollama models + Claude Access (SSO), “Only Me” policy
Open WebUI chat.spelvin.dev Chat frontend for everything in LiteLLM Access (SSO)
Leantime roadmap.spelvin.dev Project management — replaced Plane Access (SSO)
FlowViz flowviz.spelvin.dev Attack-flow diagramming Access (self-hosted), “Only Me” policy
Windmill windmill.spelvin.dev Workflow automation — replaced n8n Access (self-hosted), “Only Me” policy
Authentik auth.spelvin.dev Identity provider — passkey login for Access None — this is an identity provider, see below
ntfy ntfy.spelvin.dev Push notifications for scripts/automations None — deliberately not behind Access (see below)
knowledge-mcp mcp.spelvin.dev Remote MCP server — serves the knowledge repo and the SOC model to every Claude client (phone, web, desktop, Claude Code) Its own OAuth, delegating login to Authentik — deliberately not behind Access (see below)
forecaster (no hostname — on-demand) On-demand InfluxDB prediction jobs, run on Pam via a task scheduler, not a persistent container N/A

Every remaining app is behind Cloudflare Tunnel + Access — the Caddy/Authelia migration is done, not partial — with one deliberate exception, ntfy (see below). That wasn’t obvious until checked directly: a full DNS audit turned up that every one of these hostnames already had its own CNAME straight to the Tunnel, meaning Caddy’s matching Caddyfile blocks were already unreachable dead code before Caddy itself was even removed. The only thing still reaching Caddy was auth.spelvin.dev via a catch-all *.spelvin.dev wildcard A record — and once Outline (the last app with any Authelia-era dependency) was retired, nothing needed Authelia’s portal either. Caddy, Authelia, and that wildcard record are gone as of this writing.

auth.spelvin.dev exists again, but don’t confuse it with the old Authelia portal — it’s now Authentik, added afterward for passkey login, unrelated to and unaware of the Authelia instance that used to sit at the same hostname.

Access itself no longer gates apps by matching a specific email address — see Networking for the current model (reusable “Only Me” / household policies matched via an OIDC groups claim against Authentik group membership, not per-app email rules).

ntfy is the one app that deliberately sits outside Cloudflare Access entirely. Its whole point is accepting authenticated push requests directly from scripts and automations, not interactive browser logins — an Access login page in front of it would break that. It’s reachable straight through the Tunnel, with ntfy’s own token auth as the only gate.

knowledge-mcp is the second deliberate exception, for a different reason than ntfy. Claude’s hosted connectors are called from Anthropic’s servers, which can’t get through an Access login page, and Cloudflare’s own “Managed OAuth” for MCP servers fails against claude.ai on web and mobile (its 401 omits the WWW-Authenticate header those clients need — claude-ai-mcp#410, closed as not planned). So the server performs the MCP OAuth handshake itself (FastMCP’s OIDCProxy) and delegates the actual login to Authentik, with the same passkey flow as everything else. Who may connect is enforced by the Authentik application’s policy binding — a direct user binding, the same single-person pattern as the Grafana and Synology DSM providers — rather than by an Access policy. Note this means Bot Fight Mode and any WAF rule on the zone apply to Anthropic’s servers directly; if a connector ever fails with no login page appearing at all, suspect that first.

forecaster isn’t a Tunnel/Access app at all — it has no hostname. It’s a set of on-demand InfluxDB prediction jobs that run on Pam, invoked by a task scheduler rather than running as a persistent container.

  • Outline (outline.spelvin.dev, team wiki) — replaced by this wiki site. Had real content (31 documents, 6 collections) at the time of removal; wiped by choice, not migrated.
  • Caddy — the legacy edge reverse proxy. Removed along with its two data volumes (caddy_data/caddy_config - TLS state).
  • Authelia — the legacy SSO provider. Removed along with its config directory (bind-mounted, not a named volume, so this was a plain directory delete rather than a docker volume rm).
  • Plane → Leantime: Plane was retired in favor of Leantime. A one-off migration script (scripts/migrate-plane-to-leantime.py in docker-infra) moved projects and modules across before teardown; scripts/migration_state.json holds the ID mapping from that run. Plane’s 13 containers, its 4 data volumes, and the vendored upstream source (/opt/plane-selfhost, outside the repo) are all gone. plane-portfolio-timeline (a custom Gantt view over Plane’s API, timeline.mickeystech.com) went with it, since it had nothing left to talk to. That hostname’s DNS lives in a different zone (mickeystech.com, not spelvin.dev) this wiki’s Cloudflare access doesn’t reach — it’s still pointed at the VPS and will just fail now that nothing answers it there.
  • n8n — replaced by Windmill; nothing was migrated over (n8n’s data was wiped on removal, by choice).
  • Jupyter — personal notebook server; its work volume was confirmed empty before teardown, nothing lost. Was the last thing still behind Caddy/Authelia before Outline’s removal made both fully redundant.
  • Database/cache sidecars for the services above (Postgres, MySQL, Redis) aren’t listed individually — see each service’s own docker-compose.yml.