Setting Up Authentik as an Identity Provider for Self-Hosted Apps

Setting Up Authentik as an Identity Provider for Self-Hosted Apps

I used to manage passwords for a dozen self-hosted applications across my homelab—Nextcloud, Jellyfin, Gitea, Vaultwarden, Immich—and it was a nightmare. Every app had its own user database, password reset flows, and login page. Then I discovered Authentik, and it changed everything. Now I have a single identity provider that handles authentication for all my services, with proper OAuth2/OIDC support, passwordless login, and even multi-factor authentication. In this guide, I'll walk you through deploying Authentik and connecting your existing apps to it.

Why Authentik Instead of the Alternatives?

You've probably heard of Keycloak—it's more powerful but also heavier and overkill for most homelabs. I chose Authentik because it's purpose-built for self-hosters: lighter resource footprint, easier configuration, and great documentation. Unlike roll-your-own solutions, you get proper OAuth2/OIDC flows, which means your apps don't need to store passwords at all.

The key advantage is centralized identity management. Instead of resetting a password in five places when you change it, you change it once in Authentik and it propagates everywhere. You also get audit logs, session management, and the ability to implement policies—like requiring MFA only when accessing sensitive apps.

Prerequisites and Planning

You'll need a server with Docker and Docker Compose installed. I'm running this on a 2-core, 4GB RAM VPS from RackNerd (if you're looking for affordable hosting, RackNerd's KVM VPS is a solid choice), but Authentik runs fine on even modest hardware. You'll also need a reverse proxy—I use Caddy, but Nginx or Traefik work too. Finally, you need a domain or subdomain for Authentik itself (e.g., auth.example.com).

Authentik uses PostgreSQL as its database and Redis for caching. The full stack includes these components, which is why Docker Compose is perfect here.

Deploying Authentik with Docker Compose

Here's the production-ready Docker Compose file I use. This includes PostgreSQL, Redis, and Authentik itself, all properly networked and with persistent storage:

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    container_name: authentik-postgres
    environment:
      POSTGRES_USER: authentik
      POSTGRES_PASSWORD: ${PG_PASS}
      POSTGRES_DB: authentik
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - authentik
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    container_name: authentik-redis
    networks:
      - authentik
    restart: unless-stopped

  authentik:
    image: ghcr.io/goauthentik/authentik:2026.3
    container_name: authentik
    command: server
    environment:
      AUTHENTIK_SECRET_KEY: ${SECRET_KEY}
      AUTHENTIK_POSTGRESQL__HOST: postgres
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
      AUTHENTIK_LOG_LEVEL: info
    ports:
      - "9000:9000"
      - "9443:9443"
    depends_on:
      - postgres
      - redis
    volumes:
      - authentik_media:/media
    networks:
      - authentik
    restart: unless-stopped

  authentik-worker:
    image: ghcr.io/goauthentik/authentik:2026.3
    container_name: authentik-worker
    command: worker
    environment:
      AUTHENTIK_SECRET_KEY: ${SECRET_KEY}
      AUTHENTIK_POSTGRESQL__HOST: postgres
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_REDIS__HOST: redis
    depends_on:
      - postgres
      - redis
    volumes:
      - authentik_media:/media
    networks:
      - authentik
    restart: unless-stopped

volumes:
  postgres_data:
  authentik_media:

networks:
  authentik:
    driver: bridge

Create a .env file with secure values:

SECRET_KEY=$(openssl rand -base64 32)
PG_PASS=$(openssl rand -base64 16)
echo "SECRET_KEY=$SECRET_KEY" > .env
echo "PG_PASS=$PG_PASS" >> .env

Then start the stack:

docker-compose up -d

Authentik runs on port 9000 internally. You'll need to expose it through your reverse proxy.

Exposing Authentik Through Your Reverse Proxy

I use Caddy. Here's my configuration:

auth.example.com {
  reverse_proxy localhost:9000 {
    header_uri X-Authentik-Upstream {http.reverse_proxy.upstream.hostport}
    header_uri X-Script-Name /
  }
}

If you're using Nginx Proxy Manager, just create a proxy host pointing to localhost:9000 and enable SSL. For Traefik with Docker Compose labels, add this to your Authentik service:

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"

Once that's running, navigate to https://auth.example.com and log in with the default credentials (admin/admin—change this immediately).

Watch out: Change the default admin password immediately. Go to Admin Panel → Users, click the admin user, and set a new password. Also enable MFA on your own account right away.

Creating an OAuth2 Application for Your First App

Now I'll show you how to connect an app. Let's use Nextcloud as an example. In the Authentik Admin Panel, go to Applications → Applications and click Create.

Fill in these details:

For the provider, set:

After creating the provider, copy the Client ID and Client Secret—you'll need them in Nextcloud.

Connecting Nextcloud to Authentik

In Nextcloud, install the "Social Login" app if you haven't already. Then SSH into your Nextcloud container and edit config/config.php to add:

'social_login' => [
  'custom_oidc' => [
    'client_id' => 'YOUR_CLIENT_ID_HERE',
    'client_secret' => 'YOUR_CLIENT_SECRET_HERE',
    'scope' => 'openid profile email',
    'authorize_url' => 'https://auth.example.com/application/o/authorize/',
    'access_token_url' => 'https://auth.example.com/application/o/token/',
    'user_info_url' => 'https://auth.example.com/application/o/userinfo/',
    'icon' => 'icon-external-white.png',
  ],
],

Nextcloud will now show a "Log in with Authentik" button on the login page. The first user to log in via Authentik will be auto-provisioned as long as the email matches an existing user or a new account is being created.

Tip: To auto-create users in Nextcloud, you may need to enable auto-provisioning in the Social Login app settings. Also, ensure your Authentik users have email addresses configured, as Nextcloud needs these for account creation.

Mapping Users and Groups

By default, Authentik passes the user's email as the username. In Nextcloud, this might be awkward. You can customize what information Authentik sends using property mappings in the OAuth2 provider settings. Go to the provider and add a property mapping:

Scope Mapping: Create a new scope mapping with type "OpenID" and add custom claims. For example, to send the username instead of email, use:

preferred_username: user.username
name: user.name
email: user.email
groups: user.groups

This gives your applications full control over what user attributes they receive.

Adding Passwordless Login

One of Authentik's coolest features is passwordless authentication. In the Admin Panel, go to Flows and Stages, then create a new stage of type "Identification." Configure it to accept email and automatically redirect to a passwordless prompt. Your users can then log in by clicking a link sent to their email—no password needed.

To set this up, create a new Authentication Flow:

Add stages in order: Identification, Password (optional), and MFA (optional). Save and assign it to your applications. When users hit the login page, they can choose to authenticate with email verification instead of a password.

Securing Authentik in Production

A few critical hardening steps:

  1. Enable HTTPS only. Your reverse proxy should enforce TLS. Add this to your Caddy config: @insecure { protocol http } redir @insecure https://{host}{uri}
  2. Set AUTHENTIK_OUTPOSTS__DOCKER_TLS_VERIFICATION to false if using Docker outposts. Otherwise, keep it true.
  3. Enable audit logging. Check System → Audit Log for every login and admin action.
  4. Use strong database passwords. I generate mine with openssl rand -base64 32.
  5. Backup your database regularly. PostgreSQL backups are simple: docker-compose exec postgres pg_dump -U authentik authentik > backup.sql

Connecting Other Apps

Once you've done Nextcloud, the pattern is identical for other apps. For Jellyfin, there's a plugin. For Gitea, use OAuth2 in Admin → Settings. For Vaultwarden, use the OIDC environment variables. Each app's OAuth2 integration is slightly different, but they all follow the same principle: create an application in Authentik, get the credentials, and plug them in.

Common apps I've successfully connected:

Troubleshooting Common Issues

Blank login screen or CSS not loading: This usually means Authentik can't reach itself via HTTPS. Check that your domain resolves and your reverse proxy is configured correctly. In the Authentik logs, look for SSL verification errors.

OAuth2 callback fails with "Redirect URI mismatch": Ensure the redirect URL in Authentik exactly matches what your app is sending. Some apps add trailing slashes or port numbers—these must match byte-for-byte.

Users created but can't log in: Check that users have an email address set and that AUTHENTIK_OUTPOSTS__DOCKER is set correctly if using Docker outposts for forward auth.

Database connection refused: Make sure the PostgreSQL container has started and the password in your environment matches. Run docker-compose logs postgres to debug.

Next Steps

You now have a working identity provider. Next, I'd recommend