How to Install Authentik on Ubuntu 24.04 VPS: Self-Hosted Identity Provider
Every modern stack eventually needs a real identity layer: a single login that unlocks Grafana, Nextcloud, Portainer, your internal wiki, a dozen SaaS apps, and half a dozen ad-hoc admin panels. You can bolt basic auth onto each one and hope nobody reuses passwords, or you can put a proper identity provider in front of everything. This guide walks you through installing Authentik on an Ubuntu 24.04 VPS using Docker Compose — from SSH login to a production-grade IdP serving OIDC, SAML, LDAP, MFA, and forward-auth in under an hour.
Prefer a managed stack? Our CloudCore Professional plan gives you the headroom to run Authentik alongside your other services with room to spare. Launch a VPS now and start deploying.
Table of Contents
.envWhat is Authentik?
Authentik is an open-source identity provider written in Python and TypeScript that acts as the single source of truth for authentication and authorization across all of your applications. It speaks every protocol modern apps need — OpenID Connect (OIDC), OAuth2, SAML 2.0, LDAP, SCIM, and a proxy/forward-auth mode for protecting legacy apps that have no auth layer of their own. Instead of each app maintaining its own user database, every app trusts Authentik, and Authentik centralizes users, groups, MFA, password policies, session management, and audit logging in one place.
Under the hood, Authentik runs as a small constellation of services: a server that handles the web UI and protocol endpoints, a worker that processes background jobs (email, sync, outpost deployments, expressions), a PostgreSQL database for durable state, a Redis instance for sessions and caches, and optional outposts — lightweight proxy or LDAP containers that sit in front of specific applications and talk back to the core. This architecture lets you scale each piece independently and deploy outposts close to the apps they protect.
Feature-wise, Authentik covers the territory you expect from a commercial IdP: social login providers (Google, GitHub, Microsoft, Apple, Discord, Twitter/X, and more), enterprise SSO federation via SAML, programmable authentication flows built from stages (prompt, identification, password, MFA, consent, user-write, email verification), policy expressions written in Python for fine-grained access control, TOTP, WebAuthn/passkeys, Duo, and SMS for multi-factor auth, invitations and self-service enrollment, application entitlements and RBAC, and a full REST API plus a Terraform provider for infrastructure-as-code deployments. The project is Apache 2.0 licensed and developed in the open on GitHub.
Teams adopt Authentik for a wide range of use cases. Homelabbers use it to put a single login in front of their entire self-hosted stack — Plex, Jellyfin, Home Assistant, Grafana, Portainer. Startups use it as a free replacement for Auth0 or Okta during early-stage growth, then keep it because they outgrow the need to migrate. Enterprises deploy it behind their firewall for internal SSO, employee MFA, and federation with Microsoft Entra ID or Google Workspace. SaaS builders use Authentik as the identity backend for their own products, taking advantage of its OIDC provider, branded flows, and tenant-style isolation via brands and applications.
Why Authentik Over Authelia, Keycloak, or Auth0?
The self-hosted IdP landscape has three serious contenders: Authelia, Keycloak, and Authentik. They solve overlapping problems differently.
Authelia is a small, fast Go binary focused almost exclusively on forward-auth for reverse proxies. It does one thing well: intercept requests at Traefik or Nginx, check the user's session, and allow or deny. It does not speak SAML, has no social login providers, no admin UI for managing flows, and no programmable authentication. If that's all you need, see our companion guide on installing Authelia on Ubuntu. But the moment you need SAML to federate with a SaaS vendor, a branded signup flow, social login, or an enterprise MFA policy, you outgrow it.
Keycloak is the enterprise-grade Java alternative backed by Red Hat. It is extremely capable, battle-tested, and heavy — a 3 GB+ memory baseline, slow cold starts, and a UI that feels like it was designed in 2012. Customizing flows often means writing Java SPI extensions.
Authentik sits in the sweet spot. It has a modern React-based admin UI, flows you can edit visually, a permissive Apache 2.0 license, a ~1.5 GB memory baseline for a small deployment, and feature parity with Auth0 for the things most teams actually use (OIDC, SAML, social login, MFA, RBAC, branded login pages). Compared to Auth0, Okta, or Microsoft Entra External ID, you are trading the convenience of a managed service for flat-rate VPS pricing and complete data sovereignty.
Feature Comparison
| Feature | Authelia | Authentik | Keycloak | Auth0 (hosted) |
|---|---|---|---|---|
| OIDC provider | Limited | Yes | Yes | Yes |
| SAML 2.0 IdP | No | Yes | Yes | Yes |
| LDAP outpost / server | No | Yes | Yes | No |
| Forward-auth (Nginx/Traefik) | Yes | Yes | No (plugin) | No |
| Social login providers | No | 20+ built-in | Yes | 30+ built-in |
| MFA (TOTP / WebAuthn / Duo) | Yes | Yes | Yes | Yes |
| Visual flow editor | No | Yes | Limited | Yes |
| Custom branding per app | No | Yes | Yes | Yes |
| Typical RAM footprint | ~50 MB | ~1.5 GB | ~3 GB | N/A |
| Licensing | Apache 2.0 | Apache 2.0 | Apache 2.0 | Proprietary, per-MAU pricing |
| Monthly cost (10K users) | VPS only | VPS only | VPS only | $240+/mo |
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- SSH access to your server (PuTTY on Windows, or the built-in terminal on macOS/Linux)
- At least 2 GB of RAM — 4 GB recommended for a production deployment with outposts and worker background jobs
- At least 15 GB of free disk space for Docker images, Postgres data, and media uploads
- A domain name (e.g.
auth.yourdomain.com) with DNS pointing at your VPS — required for TLS and OIDC redirect URIs - Ports 80 and 443 open in your firewall for HTTP/HTTPS
Recommended Plan: CloudCore Professional>
For running Authentik alongside a reverse proxy and a few protected applications, we recommend the CloudCore Professional plan:>
- 6 vCPU cores
- 12 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth
- EUR 19.99/month>
This gives you enough headroom for Authentik's server + worker + Postgres + Redis stack plus half a dozen protected apps. A 2 GB plan works for evaluation, but expect slow background jobs and OOMs under load.
Connect to your server via SSH to get started:
ssh root@your-server-ipStep 1: Update System Packages
Start by updating your package index and upgrading installed packages. This ensures you have the latest security patches and that Docker installation picks up the correct dependencies.
sudo apt update && sudo apt upgrade -yExpected output (abbreviated):
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:2 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Reading package lists... Done
Building dependency tree... Done
Calculating upgrade... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.If your kernel was updated, reboot before continuing:
sudo rebootThen reconnect via SSH after a minute.
Step 2: Install Docker and Docker Compose
Authentik ships as a multi-service Docker Compose stack, so we need Docker Engine and the Compose v2 plugin. We will install from Docker's official apt repository to get the latest stable release.
If you already have Docker installed from a previous guide (for example, our Docker install walkthrough), you can skip this step and jump to Step 3.
Install dependencies and add Docker's GPG key:
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpgAdd the Docker repository:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullInstall Docker Engine, CLI, and the Compose v2 plugin:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginVerify the installation:
docker --version
docker compose versionExpected output:
Docker version 27.3.1, build ce12230
Docker Compose version v2.29.7Optionally add your user to the docker group so you can run docker without sudo:
sudo usermod -aG docker $USER
newgrp dockerIf you need a deeper dive, see our full guide on installing Docker Compose on Ubuntu.
Step 3: Download the Authentik Compose Stack
Authentik publishes a pre-built Compose file and environment template. We will download both into a dedicated directory under /opt/authentik.
Create the directory and enter it:
sudo mkdir -p /opt/authentik
cd /opt/authentikDownload the latest docker-compose.yml and the .env template from the official install docs at docs.goauthentik.io/docs/install-config/install/docker-compose:
sudo curl -o docker-compose.yml https://goauthentik.io/docker-compose.yml
sudo curl -o .env https://goauthentik.io/.envExpected output (abbreviated):
% Total % Received % Xferd Average Speed Time
100 4523 100 4523 0 0 16.3k 0 --:--:--
100 312 100 312 0 0 1130 0 --:--:--Inspect what you just downloaded:
ls -laExpected output:
-rw-r--r-- 1 root root 4523 Apr 16 10:00 docker-compose.yml
-rw-r--r-- 1 root root 312 Apr 16 10:00 .envThe docker-compose.yml defines four core services:
postgresql— PostgreSQL 16 for durable state (users, applications, providers, audit logs)redis— Redis 7 for sessions, caches, and background job queuesserver— the Authentik web server exposing ports 9000 (HTTP) and 9443 (HTTPS) by defaultworker— the background worker that runs Celery tasks for email, sync, outpost deployment, and policy expressions
./media) and custom templates (./custom-templates).Step 4: Generate Secrets and Configure .env
Authentik requires two secrets in the .env file: a strong PostgreSQL password (PG_PASS) and the Authentik secret key (AUTHENTIK_SECRET_KEY) used to sign sessions, tokens, and cookies. Never use default or short values — both need to be cryptographically random.
Generate both secrets with openssl:
echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" | sudo tee -a .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')" | sudo tee -a .envExpected output (values will differ — yours must be unique):
PG_PASS=QWxRs4+7ZbqY2hJp0nWfgKdv8cTxLmEaP3uBvN1jHrQeOmYdK9sXiJaR7UtC
AUTHENTIK_SECRET_KEY=LmWkV9qZj4aB2cEfG8hIpQrTsXyJnMbKdR0vN3uL6tCeF1oHyUiP7sAxZgWqKrE2dS5bNmLkVcQjXyHpTgRfAlso set the error-reporting preference, timezone, and image tag. Edit .env:
sudo nano /opt/authentik/.envMake sure the file contains:
PG_PASS=...generated above...
AUTHENTIK_SECRET_KEY=...generated above...Optional: opt out of anonymous error reporting to the Authentik team
AUTHENTIK_ERROR_REPORTING__ENABLED=falseOptional: set your timezone
TZ=Europe/ParisPin to a specific version rather than floating on "latest"
AUTHENTIK_TAG=2025.2.4Pinning AUTHENTIK_TAG to a specific version is strongly recommended for production. Check the latest release at github.com/goauthentik/authentik/releases before you start.
Tighten permissions on the file — it now contains secrets:
sudo chmod 600 /opt/authentik/.envStep 5: Start Authentik
With the Compose file and .env in place, bring the stack up:
cd /opt/authentik
sudo docker compose pull
sudo docker compose up -dExpected output (abbreviated):
[+] Pulling 4/4
✔ postgresql Pulled
✔ redis Pulled
✔ server Pulled
✔ worker Pulled
[+] Running 4/4
✔ Container authentik-postgresql-1 Started
✔ Container authentik-redis-1 Started
✔ Container authentik-server-1 Started
✔ Container authentik-worker-1 StartedWatch the logs until the server reports it is ready:
sudo docker compose logs -f serverYou should see lines like:
{"event": "Starting authentik server", "level": "info"}
{"event": "Bootstrap completed", "level": "info"}
{"event": "Listening on 0.0.0.0:9000", "level": "info"}Press Ctrl+C to stop tailing logs. Check service health:
sudo docker compose psExpected output:
NAME STATUS PORTS
authentik-postgresql-1 Up 1 minute (healthy) 5432/tcp
authentik-redis-1 Up 1 minute (healthy) 6379/tcp
authentik-server-1 Up 1 minute (healthy) 0.0.0.0:9000->9000/tcp, 0.0.0.0:9443->9443/tcp
authentik-worker-1 Up 1 minute (healthy)All four containers should be healthy. If postgresql is unhealthy, the most common cause is a bad PG_PASS value (special characters that your shell or Compose interpreter mangled) — regenerate it without shell-special characters if needed.
Step 6: First-Time Setup and Admin Creation
Authentik is now running on port 9000 (HTTP) and 9443 (HTTPS, with a self-signed cert). For evaluation, point your browser at:
http://your-server-ip:9000/if/flow/initial-setup/You will see the initial-setup flow. This is a one-time page where you set the password for the built-in akadmin administrator account. Pick a long, unique password — store it in a password manager.
After submitting, you will be redirected to the admin dashboard at /if/admin/. The default admin username is akadmin.
Put Authentik Behind a Reverse Proxy with TLS
Before you wire up any real applications, put Authentik behind a proper reverse proxy with a TLS certificate from Let's Encrypt. If you have not installed Nginx yet, follow our Nginx install guide first.
Create the Nginx site:
sudo tee /etc/nginx/sites-available/authentik > /dev/null <<'EOF' upstream authentik { server 127.0.0.1:9000; keepalive 10; }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;
# Authentik serves large cookies; raise the header buffer proxy_buffers 8 16k; proxy_buffer_size 32k; client_max_body_size 20m;
location / { proxy_pass http://authentik; proxy_http_version 1.1; 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_set_header Connection "upgrade"; proxy_set_header Upgrade $http_upgrade; proxy_read_timeout 3600s; } } EOF
sudo ln -s /etc/nginx/sites-available/authentik /etc/nginx/sites-enabled/ sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d auth.yourdomain.com sudo nginx -t && sudo systemctl reload nginx
You can now reach Authentik at https://auth.yourdomain.com with a valid TLS certificate.
Step 7: Create Your First OIDC Application
The cleanest way to understand Authentik is to wire up one application end-to-end. We will create an OAuth2/OpenID Connect provider and bind it to an application called "Grafana" (you can substitute any OIDC-capable app).
Create the OAuth2/OIDC Provider
https://auth.yourdomain.com/if/admin/.grafana-provider
- Authorization flow: default-provider-authorization-implicit-consent (ships built-in)
- Client type: Confidential
- Client ID: auto-generated — copy this, you will need it on the Grafana side
- Client Secret: auto-generated — copy this too
- Redirect URIs: https://grafana.yourdomain.com/login/generic_oauth
- Signing Key: authentik Self-signed Certificate
Create the Application
Grafana
- Slug: grafana
- Provider: select grafana-provider from the dropdown
- Launch URL: https://grafana.yourdomain.com
- Icon: optional — upload a Grafana logo
The application now appears on every user's My applications landing page at https://auth.yourdomain.com/if/user/.
Configure the Client Side
On your Grafana server, edit /etc/grafana/grafana.ini (or set equivalent env vars) to enable generic OAuth:
[auth.generic_oauth]
enabled = true
name = Authentik
client_id = <CLIENT_ID_FROM_AUTHENTIK>
client_secret = <CLIENT_SECRET_FROM_AUTHENTIK>
scopes = openid email profile
auth_url = https://auth.yourdomain.com/application/o/authorize/
token_url = https://auth.yourdomain.com/application/o/token/
api_url = https://auth.yourdomain.com/application/o/userinfo/Restart Grafana, visit https://grafana.yourdomain.com, and click Sign in with Authentik. You will be redirected to Authentik, prompted to authenticate as akadmin, and bounced back to Grafana logged in as a new user.
The same pattern — provider + application + redirect URI on the client — works for Nextcloud, Gitea, Vaultwarden, Portainer, AWX, Outline, Hashicorp Vault, and any other OIDC-capable app.
Step 8: Add Users, Groups, and Policies
Real deployments need more than the single akadmin account.
Create a Group
grafana-users.Create a User
grafana-users.Bind the Group to the Application
By default, any authenticated user can use any application. To restrict Grafana to members of grafana-users:
grafana-users.Now users who are not in grafana-users will see a "permission denied" screen when they try to launch Grafana from the my-apps dashboard.
Policies: Expression-Based Access
For finer-grained rules (time-of-day restrictions, IP allowlists, attribute matching), use policies. Navigate to Customization -> Policies -> Create -> Expression Policy:
# Only allow users with verified email during business hours import datetimeif not request.user.is_verified: return False
now = datetime.datetime.now() if now.weekday() >= 5: # Saturday or Sunday return False
return 8 <= now.hour < 20
Bind this policy to an application the same way you bound the group.
Step 9: Deploy a Proxy Outpost for Forward-Auth
Not every app speaks OIDC. For legacy apps, internal admin panels, or anything that only serves HTTP, Authentik offers a proxy outpost — a lightweight container that terminates auth in front of the app and forwards the authenticated request downstream. It works with Nginx's auth_request, Traefik's ForwardAuth middleware, and Caddy's forward_auth directive.
Create a Proxy Provider and Application
portainer-proxy
- External host: https://portainer.yourdomain.com
- Authentication flow: default-authentication-flow
- Authorization flow: default-provider-authorization-implicit-consent
Portainer bound to portainer-proxy.Deploy the Outpost
authentik Embedded Outpost (or create a new one).Portainer.The embedded outpost runs inside the main server container on path /outpost.goauthentik.io/. For production, create a standalone outpost container on the host where the protected app lives.
Nginx auth_request Integration
Add this snippet to the Nginx server block for portainer.yourdomain.com:
server { listen 443 ssl http2; server_name portainer.yourdomain.com;ssl_certificate /etc/letsencrypt/live/portainer.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/portainer.yourdomain.com/privkey.pem;
# Forward-auth endpoint on Authentik location /outpost.goauthentik.io { proxy_pass https://auth.yourdomain.com/outpost.goauthentik.io; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; add_header Set-Cookie $auth_cookie; auth_request_set $auth_cookie $upstream_http_set_cookie; }
location / { auth_request /outpost.goauthentik.io/auth/nginx; error_page 401 = @goauthentik_proxy_signin; auth_request_set $auth_cookie $upstream_http_set_cookie; add_header Set-Cookie $auth_cookie; auth_request_set $authentik_username $upstream_http_x_authentik_username; auth_request_set $authentik_groups $upstream_http_x_authentik_groups; auth_request_set $authentik_email $upstream_http_x_authentik_email; proxy_set_header X-authentik-username $authentik_username; proxy_set_header X-authentik-groups $authentik_groups; proxy_set_header X-authentik-email $authentik_email;
proxy_pass http://127.0.0.1:9443; # Portainer upstream proxy_set_header Host $host; }
location @goauthentik_proxy_signin { internal; add_header Set-Cookie $auth_cookie; return 302 /outpost.goauthentik.io/start?rd=$request_uri; } }
Reload Nginx:
sudo nginx -t && sudo systemctl reload nginxVisit https://portainer.yourdomain.com — you will be bounced to Authentik to log in, and on success redirected back to Portainer with the X-authentik-username header set.
Traefik ForwardAuth Middleware
For Traefik users, define a middleware and attach it to the router:
http:
middlewares:
authentik:
forwardAuth:
address: https://auth.yourdomain.com/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-emailThen on the protected router:
labels:
- "traefik.http.routers.portainer.middlewares=authentik@file"Caddy uses the equivalent forward_auth https://auth.yourdomain.com { uri /outpost.goauthentik.io/auth/caddy ... } directive.
Step 10: Configure Email (SMTP) and MFA
Email is required for password resets, invitations, and verification. MFA is required for anything you actually care about.
SMTP
/opt/authentik/.env.AUTHENTIK_EMAIL__HOST=smtp.your-provider.com
AUTHENTIK_EMAIL__PORT=587
[email protected]
AUTHENTIK_EMAIL__PASSWORD=your-smtp-password
AUTHENTIK_EMAIL__USE_TLS=true
AUTHENTIK_EMAIL__USE_SSL=false
[email protected]sudo docker compose restart server workerMFA Stages
Authentik ships with stages for TOTP (authenticator apps), WebAuthn (passkeys, hardware keys), Duo, and static recovery codes.
To require MFA for all users:
default-authentication-mfa-validation.default-authenticator-totp-setup to the flow.Users can self-enroll additional factors at https://auth.yourdomain.com/if/user/#/settings.
Step 11: Brand Customization and Flows
Authentik's login pages are fully brandable via tenants (now called brands in newer versions).
Go to System -> Brands. Edit the authentik-default brand:
- Branding title:
Yourdomain SSO - Branding logo: upload a URL or path to
/media/public/ - Branding favicon: upload
- Default flows: override authentication, invalidation, recovery, enrollment
./custom-templates/ and reference it from a flow's background or CSS customization stage.Flows are the real power feature. Every page a user sees — login, signup, password reset, MFA enrollment, consent — is a flow composed of stages. Clone default-authentication-flow, add a Prompt Stage that asks for a company name, add a User Write Stage that stores it as an attribute, and you've built a custom B2B signup in five minutes without writing code.
Step 12: Optional LDAP Outpost and Social Login
LDAP Outpost
Some legacy apps (Synology, Zimbra, older Gitlab versions, many appliances) only speak LDAP. Authentik can expose its user directory over LDAP via an LDAP outpost.
default-authentication-flow) and search group.Social Login
Users will now see a "Sign in with Google" (or GitHub, etc.) button on the login page. On first sign-in, Authentik creates a local user linked to the social identity.
Backups and Upgrades
Backups
Two things need backing up: the PostgreSQL database and the media/custom-templates directories.
Database dump (run as a cron job):
#!/bin/bash
cd /opt/authentik
docker compose exec -T postgresql pg_dump -U authentik authentik | gzip > /var/backups/authentik/authentik-$(date +%F).sql.gz
find /var/backups/authentik/ -name "authentik-*.sql.gz" -mtime +14 -deleteMedia and templates:
tar czf /var/backups/authentik/media-$(date +%F).tar.gz -C /opt/authentik media custom-templatesSchedule both via crontab -e:
0 3 * /usr/local/bin/authentik-backup.shUpgrades
Authentik releases monthly. To upgrade:
cd /opt/authentikUpdate AUTHENTIK_TAG in .env to the new version
sudo nano .env
sudo docker compose pull sudo docker compose up -d
The worker container runs database migrations automatically on startup. Watch logs:
sudo docker compose logs -f workerAlways read the release notes before upgrading across a major version boundary — flow schemas occasionally require manual intervention.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
postgresql container keeps restarting | PG_PASS contains shell-special characters Compose can't parse | Regenerate with openssl rand -hex 32 (hex only, no $ or <code> </code> ), update .env, docker compose down -v, restart</td></tr><tr><td>Outpost shows "offline" in admin UI</td><td>Embedded outpost can't reach Authentik because <code>AUTHENTIK_HOST</code> is wrong or TLS cert is untrusted</td><td>Set <code>AUTHENTIK_HOST=https://auth.yourdomain.com</code> in the outpost config, ensure LE cert is valid, or set <code>AUTHENTIK_INSECURE=true</code> for self-signed</td></tr><tr><td>Login loop after OIDC redirect</td><td>Redirect URI mismatch or cookies blocked</td><td>Verify exact URL match in Provider -> Redirect URIs (trailing slash matters). Ensure cookies are not blocked by SameSite policy on cross-site setups</td></tr><tr><td>"Invalid CSRF token" on form submit</td><td>Reverse proxy not forwarding <code>Host</code> / <code>X-Forwarded-Proto</code></td><td>Add <code>proxy_set_header Host $host;</code> and <code>proxy_set_header X-Forwarded-Proto $scheme;</code> in Nginx config</td></tr><tr><td>"502 Bad Gateway" on first login</td><td>Server container not healthy yet, or wrong upstream port</td><td><code>docker compose ps</code> — wait for <code>healthy</code>. Upstream should be <code>127.0.0.1:9000</code>, not <code>9443</code> (internal self-signed)</td></tr><tr><td>Self-signed cert warning on <code>:9443</code></td><td>Expected — Authentik generates its own cert for the internal port</td><td>Always terminate TLS at Nginx on port 9000, don't expose 9443 publicly</td></tr><tr><td>Password reset emails not arriving</td><td>SMTP misconfigured or env vars not reloaded</td><td>Test in <strong>System -> Tenants -> Test email</strong>. Check <code>docker compose logs worker</code> for SMTP errors. Restart worker after <code>.env</code> changes</td></tr><tr><td>Worker tasks stuck in "Pending"</td><td>Redis container unhealthy or network partition</td><td><code>docker compose restart redis worker</code>. Check <code>docker compose logs redis</code> for OOM</td></tr><tr><td>High memory usage (>3 GB)</td><td>Worker keeps long-lived Celery workers</td><td>Set <code>AUTHENTIK_WORKER__CONCURRENCY=2</code> in <code>.env</code> on small VPS</td></tr></tbody></table></div>
|