Skip to Content

Self-Host Authentik: Your Own Identity Provider in 20 Minutes

Self-Host Authentik: Your Own Identity Provider in 20 Minutes

Self-Host Authentik: Your Own Identity Provider in 20 Minutes

Tired of managing separate logins for every self-hosted service? Authentik is an open-source identity provider that brings enterprise-grade SSO, MFA, and user management to your homelab — without the enterprise-grade complexity (or price tag).

In this guide, you'll deploy Authentik with Docker Compose, connect it to reverse proxies like Traefik or Nginx, and secure your entire stack behind a single login.

Why Self-Host Your Identity Provider?

Every app you self-host needs authentication. Without a central identity provider, you're juggling:

  • Duplicate user accounts across services
  • Inconsistent password policies
  • No unified MFA enforcement
  • Manual user provisioning for family/team members

Authentik solves this with OIDC, SAML, LDAP, and proxy provider support — meaning virtually any self-hosted app can delegate authentication to it. Think of it as your own private Okta or Auth0.

What Makes Authentik Different?

Unlike simpler SSO solutions, Authentik offers:

  • Visual flow editor — customize login/signup flows without code
  • Policy engine — attribute-based access control
  • Multi-tenancy — separate branding and policies per domain
  • Event monitoring — audit log of every authentication attempt

Prerequisites

  • VPS or server with 2+ CPU cores, 4GB RAM (8GB recommended for production)
  • Docker and Docker Compose installed
  • A domain with DNS A record pointing to your server (e.g., auth.yourdomain.com)
  • Reverse proxy (Traefik, Nginx, or Caddy) with SSL certificates

Step 1: Create the Docker Compose File

Create a directory for Authentik and its configuration:

mkdir -p ~/authentik && cd ~/authentik
mkdir -p media custom-templates certs

Create docker-compose.yml:

version: "3.8"

services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      start_period: 20s
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - database:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${PG_PASS:?database password required}
      POSTGRES_USER: ${PG_USER:-authentik}
      POSTGRES_DB: ${PG_DB:-authentik}
    env_file:
      - .env

  redis:
    image: docker.io/library/redis:alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - redis:/data

  server:
    image: ghcr.io/goauthentik/server:2024.10
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
      - ./certs:/certs
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - "9000:9000"
      - "9443:9443"
    depends_on:
      - postgresql
      - redis

  worker:
    image: ghcr.io/goauthentik/server:2024.10
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
    user: root
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
      - ./certs:/certs
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      - postgresql
      - redis

volumes:
  database:
  redis:

Step 2: Configure Environment Variables

Create .env with secure values:

# Database Configuration
PG_USER=authentik
PG_DB=authentik
PG_PASS=$(openssl rand -base64 36 | tr -dc 'a-zA-Z0-9' | head -c 32)

# Authentik Security
AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -dc 'a-zA-Z0-9' | head -c 50)

# Email Configuration (optional, for password resets)
# AUTHENTIK_EMAIL__HOST=smtp.yourdomain.com
# AUTHENTIK_EMAIL__PORT=587
# [email protected]
# AUTHENTIK_EMAIL__PASSWORD=your_smtp_password
# AUTHENTIK_EMAIL__USE_TLS=true
# [email protected]

Generate the secrets and write them to .env:

cat > .env << 'EOF'
PG_USER=authentik
PG_DB=authentik
PG_PASS=$(openssl rand -base64 36 | tr -dc 'a-zA-Z0-9' | head -c 32)
AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -dc 'a-zA-Z0-9' | head -c 50)
EOF

Step 3: Launch Authentik

docker compose pull
docker compose up -d

Wait for the database migrations to complete (check logs):

docker compose logs -f server

Look for: Server started

Step 4: Initial Setup

Open https://your-server-ip:9443 (or http://localhost:9000 if testing locally).

You'll see a setup screen asking for:

  • Email — your admin account email
  • Password — strong admin password
  • Token — leave blank for new installations

After setup, log in at the default URL. You'll land in the Admin Interface.

Step 5: Configure Your First Application

Let's secure a simple app — say, whoami.yourdomain.com — using Authentik's proxy provider.

Create an Application

  1. Go to Applications → Applications
  2. Click Create
  3. Name: Whoami
  4. Slug: whoami
  5. Provider: Create new Proxy Provider

Configure the Proxy Provider

For the proxy provider:

  • Type: Forward auth (single application)
  • External host: https://whoami.yourdomain.com
  • Internal host: http://whoami:80 (your app's internal address)

Update Your Reverse Proxy

For Traefik, add this middleware to your whoami service:

labels:
  - "traefik.http.middlewares.authentik.forwardauth.address=http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
  - "traefik.http.middlewares.authentik.forwardauth.trustForwardHeader=true"
  - "traefik.http.middlewares.authentik.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-entitlements,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version"
  - "traefik.http.routers.whoami.middlewares=authentik"

For Nginx, add to your server block:

location / {
    proxy_pass http://whoami:80;
    
    # Authentik forward auth
    auth_request /outpost.goauthentik.io/auth/nginx;
    error_page 401 = @goauthentik_proxy_signin;
    auth_request_set $x_authentik_username $upstream_http_x_authentik_username;
    auth_request_set $x_authentik_groups $upstream_http_x_authentik_groups;
    auth_request_set $x_authentik_email $upstream_http_x_authentik_email;
    auth_request_set $x_authentik_name $upstream_http_x_authentik_name;
    auth_request_set $x_authentik_uid $upstream_http_x_authentik_uid;
    
    proxy_set_header X-authentik-username $x_authentik_username;
    proxy_set_header X-authentik-groups $x_authentik_groups;
    proxy_set_header X-authentik-email $x_authentik_email;
    proxy_set_header X-authentik-name $x_authentik_name;
    proxy_set_header X-authentik-uid $x_authentik_uid;
}

location /outpost.goauthentik.io {
    proxy_pass http://authentik-server:9000/outpost.goauthentik.io;
    proxy_set_header Host $host;
    proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Step 6: Test the Flow

  1. Navigate to https://whoami.yourdomain.com
  2. You should be redirected to Authentik's login page
  3. Log in with your admin credentials
  4. You're redirected back to the app — now authenticated

Check the headers to confirm Authentik is passing identity data:

curl -H "X-authentik-username: test" https://whoami.yourdomain.com

Step 7: Add MFA (Recommended)

Enforce two-factor authentication for all users:

  1. Go to Policies → Policy Bindings
  2. Create a policy: Group → Users → All users
  3. Bind to default-authentication-flow
  4. Set Must pass: True

Users will now be prompted to set up TOTP or WebAuthn on next login.

Verification Checklist

  • [ ] Authentik admin interface loads at https://auth.yourdomain.com
  • [ ] Can create users and groups in Admin Interface
  • [ ] Proxy provider redirects unauthenticated requests to login
  • [ ] After login, user is redirected back to original app
  • [ ] App receives X-authentik-username header
  • [ ] MFA prompt appears if policy is enforced

Next Steps

Now that Authentik is running:

  • Connect more apps — Grafana, Portainer, and Nextcloud all support OIDC
  • Set up LDAP — for apps that don't support modern protocols
  • Create user groups — map to different access levels per app
  • Enable email — for password resets and notifications
  • Monitor events — check Events for failed login attempts

Related Guides

  • Self-Host Portainer: Manage All Your Docker Containers from One Dashboard in 15 Minutes
  • Self-Host WireGuard: Your Own Secure VPN in 10 Minutes
  • Self-Host Traefik: The Ultimate Reverse Proxy for Your Homelab

Questions or hit a snag? Drop a comment below or reach out on our community channels.

Self-Host WireGuard: Your Own Secure VPN in 10 Minutes
Deploy a private WireGuard VPN server with Docker in 10 minutes