Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Self-Hosted Authentik SSO Setup Guide

A complete guide to setting up Authentik as a self-hosted Single Sign-On (SSO) and OAuth2/OIDC identity provider with YubiKey MFA support.

Table of Contents

  1. Overview
  2. Prerequisites
  3. Installation
  4. Reverse Proxy Configuration
  5. Initial Setup
  6. YubiKey MFA Configuration
  7. Integration with Applications
  8. Troubleshooting

Overview

What is Authentik?

Authentik is a powerful, open-source identity provider (IdP) that provides:

  • Single Sign-On (SSO) for multiple applications
  • OAuth2 and OIDC provider capabilities
  • Multi-Factor Authentication (MFA) including WebAuthn/FIDO2 for hardware keys
  • User management with flows and policies
  • LDAP and SAML support

Use Cases:

  • Centralized authentication for self-hosted services
  • Replace cloud providers like Auth0 or Okta
  • Hardware security key (YubiKey) authentication
  • Fine-grained access control with policies

Prerequisites

Required Infrastructure

  • Linux server (VPS or homelab) with Docker
  • Reverse proxy (Traefik, Caddy, nginx, or Pangolin)
  • Domain name with DNS configured (e.g., auth.example.com)
  • SSL/TLS certificates (handled by reverse proxy)

Required Software

  • Docker and Docker Compose
  • Container orchestration tool (Dockge, Portainer, or docker-compose CLI)

System Requirements

  • RAM: Minimum 1GB, recommended 2GB+
  • Storage: ~500MB for containers, additional space for database
  • CPU: 1 core minimum, 2+ recommended

Installation

Step 1: Prepare Directory Structure

Create the configuration directory:

# Using /opt/configs as the base path
mkdir -p /opt/configs/authentik/{database,redis,media,certs,custom-templates}

Note: If you use a different base path, adjust the CONFIG_PATH environment variable accordingly.

Step 2: Generate Secret Key

Generate a secure random secret key for Authentik:

openssl rand -base64 60

Save this key - you’ll need it in the next step.

Step 3: Create Docker Compose File

Create /opt/stacks/authentik/compose.yaml (or use your container orchestration tool):

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

  redis:
    image: docker.io/library/redis:alpine
    container_name: authentik-redis
    command: --save 60 1 --loglevel warning
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
      - ${CONFIG_PATH}/authentik/redis:/data
    networks:
      - authentik-internal

  server:
    image: ghcr.io/goauthentik/server:2024.12.1
    container_name: authentik
    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}
      AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
      AUTHENTIK_COOKIE_DOMAIN: ${AUTHENTIK_DOMAIN:-auth.example.com}
    volumes:
      - ${CONFIG_PATH}/authentik/media:/media
      - ${CONFIG_PATH}/authentik/custom-templates:/templates
    ports:
      - "9000:9000"
      - "9443:9443"
    depends_on:
      - postgresql
      - redis
    networks:
      - authentik-internal
      - web  # External network for reverse proxy

  worker:
    image: ghcr.io/goauthentik/server:2024.12.1
    container_name: authentik-worker
    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}
      AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
    user: root
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ${CONFIG_PATH}/authentik/media:/media
      - ${CONFIG_PATH}/authentik/certs:/certs
      - ${CONFIG_PATH}/authentik/custom-templates:/templates
    depends_on:
      - postgresql
      - redis
    networks:
      - authentik-internal

networks:
  authentik-internal:
  web:
    external: true  # Reverse proxy network

Step 4: Create Environment File

Create a .env file in the same directory:

# Config directory path
CONFIG_PATH=/opt/configs

# PostgreSQL credentials
PG_PASS=your-secure-postgres-password-here
PG_USER=authentik
PG_DB=authentik

# Authentik configuration
AUTHENTIK_SECRET_KEY=paste-your-generated-secret-key-here
AUTHENTIK_DOMAIN=auth.example.com

Important: Replace the placeholder values with:

  • A strong PostgreSQL password
  • The secret key you generated in Step 2
  • Your actual domain name

Step 5: Start Authentik

cd /opt/stacks/authentik
docker compose up -d

Watch the logs to ensure migrations complete:

docker logs authentik-worker -f

You should see database migrations running. This is normal on first startup and may take 2-5 minutes. Wait for them to complete.


Reverse Proxy Configuration

Authentik requires a reverse proxy to handle SSL/TLS termination. Here are examples for common reverse proxies.

Pangolin

If using Pangolin reverse proxy:

  1. Create a new resource in Pangolin
  2. Configure:
    • Domain: auth.example.com
    • Target Address: authentik (container name)
    • Target Port: 9000
    • Authentication: Disabled (Authentik handles its own auth)
    • SSL/HTTPS: Enabled

Important: The authentik container must be on the same Docker network as Pangolin (typically called web).

Traefik (File Provider)

Add to your dynamic_config.yml:

http:
  routers:
    authentik:
      rule: "Host(`auth.example.com`)"
      service: authentik
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

  services:
    authentik:
      loadBalancer:
        servers:
          - url: "http://authentik:9000"

Traefik (Docker Provider)

Add labels to the server container:

  server:
    # ... existing config ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.authentik.rule=Host(`auth.example.com`)"
      - "traefik.http.routers.authentik.entrypoints=websecure"
      - "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
      - "traefik.http.services.authentik.loadbalancer.server.port=9000"

Nginx

server {
    listen 443 ssl http2;
    server_name auth.example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://localhost:9000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        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;
    }
}

Caddy

auth.example.com {
    reverse_proxy authentik:9000
}

Initial Setup

Step 1: Access Initial Setup Flow

Navigate to: https://auth.example.com/if/flow/initial-setup/

Note: The path /if/flow/initial-setup/ is important - don’t forget it!

Step 2: Create Admin Account

Fill in the form:

  • Email: Your email address
  • Username: Choose an admin username (e.g., admin or your name)
  • Password: Strong password (you can change this later or disable password auth after setting up YubiKey)

Click Continue

Step 3: Verify Installation

After creating the admin account, you’ll be logged in. You should see the Authentik admin interface.

Navigate through:

  • Dashboard: Overview of your Authentik instance
  • Directory → Users: Shows your admin user
  • Applications: Empty for now (you’ll add apps here later)

YubiKey MFA Configuration

Step 1: Navigate to User Settings

  1. Click your username in the top right
  2. Select Settings

Step 2: Enroll First YubiKey

  1. Scroll to the MFA Devices section
  2. Click Enroll new device
  3. Select WebAuthn device (not TOTP)
    • WebAuthn/FIDO2 is the modern standard for hardware security keys
    • TOTP is for app-based authenticators like Google Authenticator
  4. Give it a friendly name (e.g., “YubiKey 5 - Primary”)
  5. Click Enroll
  6. Your browser will prompt you to insert and touch your YubiKey
  7. Touch the YubiKey when it blinks

Repeat the enrollment process for a second YubiKey:

  1. Enroll new device → WebAuthn device
  2. Name it (e.g., “YubiKey 5 - Backup”)
  3. Touch the second YubiKey when prompted

Why a backup? If you lose or break your primary YubiKey and haven’t enrolled a backup, you could be locked out.

Step 4: Make MFA Mandatory (Optional)

Warning: Only do this after successfully testing MFA login with your YubiKey!

  1. Navigate to Flows & Stages

  2. Find default-authentication-flow

  3. Click the ▶ (play/execute) icon to view stage bindings

  4. You’ll see stages:

    • Order 10: Identification
    • Order 20: Password
    • Order 30: MFA Validation
    • Order 100: User Login
  5. Click on default-authentication-mfa-validation stage (Order 30)

  6. Click Edit Stage (not Edit Binding)

  7. In the stage configuration:

    • Device classes: Ensure “WebAuthn” is selected
    • Other settings can remain default
  8. Click Update

  9. To make MFA required (not optional):

    • Click Edit Binding on the MFA Validation stage
    • Change Order to ensure it runs after password
    • Set Required to true if available, or
    • Create a policy that requires MFA for all users

Step 5: Test MFA Login

Before making MFA mandatory, test it works:

  1. Log out of Authentik
  2. Log back in with username and password
  3. You should be prompted for MFA
  4. Insert your YubiKey and touch it
  5. You should be logged in successfully

If this works, you can safely make MFA mandatory.


Integration with Applications

Creating an OAuth2/OIDC Provider

Authentik can provide SSO for other applications using OAuth2/OIDC. Here’s how:

Step 1: Create Provider

  1. Navigate to Applications → Providers
  2. Click Create
  3. Select OAuth2/OpenID Provider
  4. Configure:
    • Name: Application name (e.g., “Netbird”)
    • Authentication flow: default-authentication-flow
    • Authorization flow: default-provider-authorization-explicit-consent
    • Client type: Confidential
    • Redirect URIs/Origins (RegEx):
      https://app.example.com/oauth2/callback
      https://app.example.com/auth
      
    • Signing Key: authentik Self-signed Certificate
  5. Click Create
  6. Copy the Client ID and Client Secret - you’ll need these for your application

Step 2: Create Application

  1. Navigate to Applications → Applications
  2. Click Create
  3. Configure:
    • Name: Application name
    • Slug: app-name (lowercase, no spaces)
    • Provider: Select the provider you just created
  4. Click Create

Step 3: Configure Your Application

In your application’s settings, configure OAuth/OIDC:

  • Provider Type: Generic OIDC or Authentik (if available)
  • Issuer URL: https://auth.example.com/application/o/app-name/
  • Client ID: From Step 1
  • Client Secret: From Step 1
  • Scopes: openid profile email

Note: The Issuer URL format is important - it must end with /application/o/<slug>/

Example: Integrating with Netbird

# Netbird configuration
IdpManagerConfig:
  ManagerType: "authentik"
  ClientConfig:
    Issuer: "https://auth.example.com/application/o/netbird/"
    ClientID: "your-client-id"
    ClientSecret: "your-client-secret"
    RedirectURLs:
      - "https://netbird.example.com/oauth2/callback"
    Scopes:
      - "openid"
      - "profile"
      - "email"

Troubleshooting

Container Startup Issues

Migrations taking a long time:

  • This is normal on first startup (2-5 minutes)
  • Watch logs: docker logs authentik-worker -f
  • Migrations should complete and show “OK”

Containers won’t start:

# Check all containers
docker ps -a | grep authentik

# View logs
docker logs authentik
docker logs authentik-worker
docker logs authentik-db
docker logs authentik-redis

Reverse Proxy Issues

502 Bad Gateway:

  • Verify Authentik is running: docker ps | grep authentik
  • Test locally: curl http://localhost:9000/-/health/live/
    • Should return: {"status": "ok"}
  • If using containerized reverse proxy:
    • Ensure both are on the same Docker network
    • Use container name instead of localhost
    • Example: http://authentik:9000 instead of http://localhost:9000

SSL/Certificate errors:

  • Verify your reverse proxy SSL configuration
  • Check DNS points to correct server
  • Test with curl -v https://auth.example.com

Authentication Issues

Can’t access initial setup:

  • Ensure you’re accessing: https://auth.example.com/if/flow/initial-setup/
  • Check reverse proxy is forwarding correctly
  • Verify AUTHENTIK_COOKIE_DOMAIN matches your domain

Locked out after enabling MFA:

  • If you didn’t enroll a backup device, you’ll need to reset
  • Access PostgreSQL database and disable MFA:
    docker exec -it authentik-db psql -U authentik authentik
    
  • Better: Always enroll 2+ YubiKeys before making MFA mandatory

YubiKey not recognized:

  • Ensure browser supports WebAuthn (Chrome, Firefox, Edge do)
  • Check YubiKey is inserted properly
  • Try a different USB port
  • Test YubiKey at https://demo.yubico.com/webauthn

Network Configuration

Containers can’t communicate:

# Verify network exists
docker network ls | grep web

# Inspect network
docker network inspect web

# Verify authentik is on the network
docker network inspect web | grep authentik

Fix: Container not on network

# Stop stack
docker compose down

# Update compose.yaml to include web network
# See Installation section for correct configuration

# Restart
docker compose up -d

Advanced Configuration

Custom Flows

Authentik uses “flows” to control authentication, authorization, and enrollment processes. You can customize these:

  1. Navigate to Flows & Stages
  2. Duplicate existing flows to create custom versions
  3. Add/remove stages to modify behavior
  4. Bind policies to stages for conditional logic

User Self-Service Registration

Enable users to create their own accounts:

  1. Navigate to Flows & Stages → Flows
  2. Find default-source-enrollment flow
  3. Click Edit
  4. Configure stages to collect user information
  5. Set Create users as inactive if you want admin approval
  6. Share enrollment URL: https://auth.example.com/if/flow/default-source-enrollment/

LDAP Support

Authentik can act as an LDAP server:

  1. Navigate to Applications → Outposts
  2. Create LDAP outpost
  3. Bind to users and groups
  4. Configure client applications to use Authentik LDAP

SAML Provider

For applications requiring SAML instead of OIDC:

  1. Navigate to Applications → Providers
  2. Create SAML Provider
  3. Configure metadata URL
  4. Set ACS URL and audience from your application

Security Best Practices

  1. Use strong passwords for PostgreSQL and Authentik admin
  2. Enable MFA for all users, especially administrators
  3. Enroll multiple YubiKeys for redundancy
  4. Regular backups of /opt/configs/authentik/database
  5. Update regularly: docker compose pull && docker compose up -d
  6. Monitor logs for suspicious activity
  7. Use separate networks for internal communication
  8. Limit outpost exposure - only expose necessary ports
  9. Review policies regularly to ensure proper access control
  10. Test recovery procedures before you need them

Backup and Recovery

Backup

Critical data to backup:

  • PostgreSQL database: /opt/configs/authentik/database
  • Media files: /opt/configs/authentik/media
  • Environment file: .env (contains secrets!)

Backup command:

# Stop containers
docker compose down

# Create backup
tar -czf authentik-backup-$(date +%Y%m%d).tar.gz /opt/configs/authentik .env

# Restart
docker compose up -d

Recovery

# Stop existing installation
docker compose down

# Restore data
tar -xzf authentik-backup-YYYYMMDD.tar.gz -C /

# Start containers
docker compose up -d