Baseline Setup, Part 4 – Centralized Authentication with Authentik

How I set up centralized authentication using Authentik for my homelab. With SSO, role-based access, and Traefik middleware, I finally have a login system that just works.

Baseline Setup, Part 4 – Centralized Authentication with Authentik
Photo by Markus Spiske / Unsplash

Baseline Setup, Part 4 – Centralized Authentication with Authentik

Routing was the easy part—now it’s time to lock things down.

I’ve used basic auth, IP allowlists, and even some homegrown token-based hacks in the past. But nothing has made managing access across a dozen services as smooth as Authentik.

This post walks through how I deployed Authentik outside the Kubernetes cluster (on purpose), how I wired it into services like Traefik and Homepage, and why this kind of central login system is absolutely worth it for a homelab at scale.


🧠 Why Authentik?

Because SSO shouldn’t require Google Workspace.

Authentik gives me:

  • Centralized authentication with user/group management
  • Built-in OAuth2, SAML, and reverse proxy support
  • Direct integration with Traefik middleware
  • Per-service policy control (think: roles, labels, conditions)
  • A clean UI that doesn’t feel like an afterthought

Plus, it’s open-source and actively developed. And it just plain works.


🧱 Where I Run It

Unlike most of my services, Authentik doesn’t live in Kubernetes.

I run it directly on Overseer, my external Ansible/admin box, using Docker Compose. That keeps it isolated from cluster failures and lets me patch or reboot nodes without locking myself out of access controls.


🧰 Docker Compose Setup

Here’s the simplified stack:

version: '3.4'

services:
  postgresql:
    image: postgres:12-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: authentik
      POSTGRES_USER: authentik
      POSTGRES_PASSWORD: changeme
    volumes:
      - db_data:/var/lib/postgresql/data

  redis:
    image: redis:alpine
    restart: unless-stopped

  server:
    image: authentik/server:latest
    restart: unless-stopped
    ports:
      - "9000:9000"  # UI
      - "9443:9443"  # proxy
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_SECRET_KEY: super-secret-key
    depends_on:
      - postgresql
      - redis

volumes:
  db_data:

I have secrets and extra config in an .env file, and it’s all managed via Overseer playbooks now.


🔄 Reverse Proxy + Traefik Integration

Authentik has a built-in reverse proxy provider that plugs right into Traefik using middleware. You define an application in Authentik, generate the middleware snippet, and reference it in your TOML file like this:

[http.middlewares.authentik.forwardAuth]
  address = "https://auth.petieclark.com/outpost.goauthentik.io/auth/traefik"
  trustForwardHeader = true
  authResponseHeaders = ["X-authentik-username", "X-authentik-email"]

Then just attach that middleware to your router:

[http.routers.dashboard]
  rule = "Host(`dashboard.petieclark.com`)"
  entryPoints = ["websecure"]
  service = "homepage"
  middlewares = ["authentik"]
  tls = true

Boom. Now the service is behind your Authentik login.


🔐 Groups, Policies, and SSO Flow

I have two core groups:

  • admin – full access to everything
  • family – limited dashboard-only access

Services are linked to policies, which check group membership or IP range. I can also limit access to certain apps by time, browser type, or even location if I want to get fancy.

Login flows are standard OAuth2 or reverse proxy headers. You can also customize the UI, which is a nice touch.


🧠 Lessons Learned

  • Run Authentik outside the cluster—it’s your lifeline
  • Always persist your Postgres volume
  • Set up email notifications early (password resets, alerts)
  • Traefik + Authentik is a dream combo once configured

🔜 Coming Up Next

Now that routing and access are dialed in, I’ll walk through Homepage, my central dashboard. It's what I use to keep tabs on everything running—from uptime to service health to quick links.