How to Deploy Authentik on a VPS: Self-Hosted SSO and Identity Provider for Your Apps

How to Deploy Authentik on a VPS: Self-Hosted SSO and Identity Provider for Your Apps

Authentik is a modern, open-source identity provider with a strong focus on flexibility and ease of use — it handles SSO via OAuth2/OIDC/SAML, provides an LDAP server for legacy applications, supports SCIM for user provisioning, and offers a visual flow editor for custom authentication policies (MFA, captcha, enrollment). It’s a compelling alternative to Keycloak and Zitadel for teams wanting a highly customizable authentication platform.

Authentik vs Keycloak vs Zitadel

  • Authentik: Python/Django, visual flow editor for auth policies, strong LDAP proxy, excellent Docker experience, good documentation
  • Keycloak: Java, most features, most complex, best for enterprise with SAML requirements
  • Zitadel: Go, simplest operations, best M2M auth and passkeys, least customizable auth flows
  • Choose Authentik: If you need custom authentication flows, LDAP proxy for legacy apps, or want a Python-based platform

Step 1: Docker Compose Setup

<code">mkdir -p /opt/authentik && cd /opt/authentik

# Download the official docker-compose file
wget https://goauthentik.io/docker-compose.yml

# Generate a secret key and password
echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')" >> .env
echo "AUTHENTIK_ERROR_REPORTING__ENABLED=false" >> .env

cat .env   # Verify settings

chmod 600 .env
<code">nano docker-compose.yml

Key sections to verify in the downloaded file:

<code"># The official compose includes:
# - postgresql (database)
# - redis (caching and queues)
# - authentik-server (main application on port 9000)
# - authentik-worker (background tasks)

# Add custom environment variables:
services:
  authentik-server:
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
      # Email (for password resets and invitations)
      AUTHENTIK_EMAIL__HOST: smtp.mailgun.org
      AUTHENTIK_EMAIL__PORT: 587
      AUTHENTIK_EMAIL__USERNAME: postmaster@mg.yourdomain.com
      AUTHENTIK_EMAIL__PASSWORD: your_smtp_password
      AUTHENTIK_EMAIL__USE_TLS: "true"
      AUTHENTIK_EMAIL__FROM: authentik@yourdomain.com
    ports:
      - "127.0.0.1:9000:9000"
      - "127.0.0.1:9443:9443"
<code">docker compose up -d
docker compose logs -f authentik-server   # Wait for startup (~60 seconds)

Step 2: Nginx Reverse Proxy

<code">sudo nano /etc/nginx/sites-available/authentik
<code">server {
    listen 80;
    server_name auth.yourdomain.com;
    return 301 https://$host$request_uri;
}

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

    ssl_certificate /etc/letsencrypt/live/auth.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1: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;
        proxy_read_timeout 300s;
    }
}
<code">sudo ln -s /etc/nginx/sites-available/authentik /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d auth.yourdomain.com

Step 3: Create Admin Account

<code"># Create initial admin user
docker compose exec authentik-worker ak create_admin_group
docker compose exec authentik-worker ak shell -c "
from authentik.core.models import User
u = User.objects.create_superuser('admin', 'admin@yourdomain.com', 'YourSecurePassword!')
print('Admin created:', u.username)
"
  1. Visit https://auth.yourdomain.com/if/flow/initial-setup/
  2. Or log in at https://auth.yourdomain.com with the credentials above
  3. Navigate to Admin → System → Tenants → set your domain

Step 4: Configure OAuth2/OIDC Provider

Create an OAuth2 application so services can authenticate via Authentik:

  1. Authentik Admin → Applications → Providers → Create → OAuth2/OpenID Provider
  2. Name: Grafana, Client Type: Confidential
  3. Redirect URIs: https://grafana.yourdomain.com/login/generic_oauth
  4. Copy Client ID and Client Secret
  5. Applications → Create → name: Grafana, provider: select Grafana provider

Grafana Integration

<code"># grafana.ini
[auth.generic_oauth]
enabled = true
name = Authentik
client_id = YOUR_CLIENT_ID
client_secret = YOUR_CLIENT_SECRET
scopes = openid email profile
auth_url = https://auth.yourdomain.com/application/o/grafana/authorize/
token_url = https://auth.yourdomain.com/application/o/grafana/token/
api_url = https://auth.yourdomain.com/application/o/userinfo/
allow_sign_up = true
role_attribute_path = contains(groups[*], 'Grafana Admins') && 'Admin' || 'Viewer'

Gitea Integration

<code"># In Gitea admin panel → Authentication Sources → Add Authentication Source
# Type: OAuth2
# Provider: OpenID Connect
# Client ID: YOUR_CLIENT_ID
# Client Secret: YOUR_CLIENT_SECRET
# OpenID Connect Auto Discovery URL:
#   https://auth.yourdomain.com/application/o/gitea/.well-known/openid-configuration

Step 5: LDAP Outpost (Legacy App Support)

Authentik can present an LDAP interface for applications that don’t support OAuth2/OIDC:

  1. Admin → Outposts → Create → type: LDAP
  2. Bind DN: cn=ldapservice,ou=serviceaccounts,dc=ldap,dc=yourdomain,dc=com
  3. The LDAP outpost runs as a Docker container on port 389
<code"># Add LDAP outpost to docker-compose.yml:
services:
  authentik-ldap:
    image: ghcr.io/goauthentik/ldap:latest
    ports:
      - "127.0.0.1:389:3389"
    environment:
      AUTHENTIK_HOST: https://auth.yourdomain.com
      AUTHENTIK_INSECURE: "false"
      AUTHENTIK_TOKEN: your_outpost_token

Step 6: MFA Policy Flow

  1. Admin → Flows → Flows → find “default-authentication-flow”
  2. Edit → Stage Bindings → Add MFA stage
  3. Available MFA stages: TOTP (Google Authenticator), WebAuthn (passkeys/hardware keys), Email OTP, SMS OTP
  4. Set as optional (users can enroll) or mandatory (required for all users)

Getting Started

Authentik needs 1–2 GB RAM for the server, worker, PostgreSQL, and Redis. A 2 GB Ubuntu VPS at VPS.DO handles Authentik with headroom for a dozen connected applications. The Python/Django stack is familiar to most developers for debugging and customization. For teams running Grafana, Gitea, Outline, and Nextcloud, Authentik provides one-click SSO setup for each with a standardized OAuth2/OIDC flow.

Conclusion

Authentik’s visual flow editor, LDAP proxy, and strong OAuth2/OIDC support make it the most flexible self-hosted identity provider for heterogeneous application stacks — supporting both modern OAuth2 apps and legacy LDAP-only applications from a single platform. The Docker Compose deployment is maintained officially, updates are frequent, and the documentation covers integrations for dozens of common applications. For teams ready for SSO across all internal tools, Authentik is the recommended starting point.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!