How to Self-Host authentik for SSO
Every self-hosted service you run has some blast radius when it falls over. The wiki goes down and people are annoyed. The CI runner goes down and deploys stop. The identity provider goes down and nobody can log into the wiki, the CI system, the dashboard you would use to diagnose the identity provider, or the ticketing system where you would file the incident. Self-hosting an identity provider is not the same category of decision as self-hosting anything else, because it is the one service that sits underneath the recovery path for all the others.
I'm Ajay, I build PandaStack. This post is a practical guide to running authentik yourself: what the components are and why there are more of them than you expected, the abstraction it uses to model logins — which is genuinely unusual and genuinely worth learning — and the four operational promises you make the moment you put it in front of something real. Then a walkthrough of deploying it on PandaStack, including a feature of ours I am going to tell you to switch off.
What authentik is, and what it is not
authentik is an open-source identity provider. It speaks OAuth2 and OpenID Connect, SAML, LDAP, RADIUS and SCIM; it can act as a source as well as a provider, federating to Google, Microsoft Entra, GitHub, generic OIDC and generic SAML upstreams; and it can put a login in front of applications that have no authentication support at all, through a reverse proxy or a forward-auth integration with the proxy you already run. It is Python and Django on the inside with Go for the proxy pieces, and it is distributed primarily as container images.
What it is not is a drop-in user database with a login form bolted on. authentik models authentication as a configurable pipeline, and that design decision explains almost everything about the product: why the admin interface has more nouns than you expected, why the answer to "how do I add MFA" is "insert a stage into a flow" rather than "tick a box," and why people who learn the model end up liking it a great deal more than people who click around hoping to find the setting.
The component model: what you are actually deploying
Open the upstream compose file and you find four services, plus a fifth category that lives somewhere else entirely. Each one exists for a specific reason, and the reason tells you what happens when it is missing.
- The server — the web process. It serves the admin interface, the user-facing login flows, and the protocol endpoints that relying parties actually call: the OIDC discovery document, the authorize and token endpoints, the SAML metadata and SSO endpoints. It also hosts the embedded outpost, which is why a small deployment can do proxy authentication without running anything extra. This is the part everyone pictures when they say "authentik."
- The worker — background processing, and much more load-bearing than its name implies. It runs scheduled tasks, applies blueprints, reconciles outposts, sends email, rotates certificates and performs the housekeeping that keeps the system consistent. A deployment with no worker boots cleanly, serves a login page, and then quietly fails to do anything asynchronous — including sending the password-reset email your user is currently waiting for.
- PostgreSQL — the state of record. Users, groups, credentials, MFA enrolments, flows, stages, policies, providers, applications, tokens and certificates. This is the database whose loss is not an inconvenience but an extinction event, and I will come back to it, at length, because it is the single most important thing in this post.
- Redis — cache, task queue broker and channel layer. Sessions, transient flow state, task dispatch and websocket coordination live here. Losing Redis logs people out and interrupts in-flight logins; it does not lose identities. That asymmetry is useful: it tells you exactly how much you need to care about each one.
- Outposts — separate processes that enforce authentik's decisions somewhere else. The proxy outpost sits in front of an application (or answers your reverse proxy's forward-auth subrequest), the LDAP outpost presents a real LDAP server to software that only speaks LDAP, and the RADIUS outpost does the same for network gear and VPNs. They connect back to the authentik core over an outbound connection and can be deployed next to the thing they protect, in a different network, or on a different continent.
There is one more piece of state that is easy to miss: media. Application icons, background images and any assets you upload live on a filesystem path inside the server and worker containers, not in Postgres. On a platform that gives you a fresh machine on every deploy, an icon uploaded through the admin interface is an icon you lose at the next release. Either keep those assets in the repository that builds your deployment, or point authentik at object storage. It is a small thing that produces a confusing bug report six weeks later.
The abstraction: flows, stages, policies, providers, applications
This is the section that makes authentik click, and it is worth reading even if you plan to use the defaults, because the defaults are themselves expressed in this model and you will eventually need to modify one.
A login is a flow execution, not a form
In most identity products, the login page is a fixed thing with settings attached. In authentik, a login is the execution of a flow: an ordered sequence of stages, each of which either collects something from the user, checks something, or performs an action. Identification collects a username or email. Password collects and validates a password. Authenticator validation demands a second factor. Consent shows the "this application wants access to your profile" screen. Prompt collects arbitrary fields you define. Email sends a link and waits for it to be clicked. User login is the stage that actually establishes the session.
Flows have a designation that says what they are for — authentication, authorization, enrollment, recovery, invalidation, and so on — and authentik ships with a default set so that a fresh install works. The important consequence is that changing how login works is a data change, not a code change or a config-file change. Want to require MFA for everyone? Add an authenticator validation stage to the authentication flow. Want self-service signup? Build an enrollment flow with a prompt stage and a user write stage and bind it to your login page. Want a captcha only for users failing repeatedly? Same mechanism, plus a policy.
The cost of this power is that a misconfigured flow is a broken login for everyone, applied instantly, with no deploy in between. There is no pull request in front of a change made in the admin interface. Which is exactly why the next paragraph exists.
Policies are the conditionals
A policy answers a yes-or-no question about the current context and can be bound to almost anything: to a stage inside a flow (so the stage only runs when the policy passes), to a flow itself (so the flow is only usable by certain people), or to an application (so only some users see it and can authenticate to it). Policies come in several kinds — group membership, expression policies written in Python, password strength, reputation based on failed attempts, GeoIP conditions — and they compose with bindings that have an order.
This is where authentik's model earns its keep. "Require hardware MFA for anyone in the infrastructure group, allow TOTP for everyone else, and refuse enrollment entirely from outside these countries" is three policy bindings rather than a feature request. It is also, and I say this with affection, an expression policy language that runs Python you wrote, in the login path, for every user. Test it. The failure mode of a syntax error in an expression policy bound to your authentication flow is not subtle.
Providers versus applications — the distinction everyone gets wrong first
This trips up nearly everyone, so here it is plainly. A provider is the protocol implementation: an OAuth2/OIDC provider with its client ID, client secret, redirect URIs, signing key and scopes; a SAML provider with its entity ID, ACS URL and assertion signing; an LDAP provider that an outpost turns into a listening LDAP server; a proxy provider that guards an upstream URL. The provider is how the relying party talks to authentik.
An application is the thing a human sees. It has a name, an icon, a slug, a launch URL, and it wraps exactly one provider. Crucially, it is also where access control lives: policy bindings on the application decide who is allowed to use it, and it is what appears in the user's application library. You create both. Creating only a provider gives you working protocol endpoints that nobody is authorized to use and that appear nowhere; creating only an application gives you a tile that does not log anyone in.
The practical rule: provider is the wire, application is the door. Authorization decisions belong on the door.
An authentik provider with no application is a perfectly functional OIDC endpoint that nobody is allowed to walk through. This is not a bug, and it is the first hour of everybody's first deployment.
The four promises you are making
Now the part that decides whether self-hosting authentik is a good idea for you specifically. These are not warnings about authentik's quality — it is well-built software with an active project behind it. They are the consequences of being the thing that everything else trusts.
1. You now own backups of the identity database
Everything that makes your authentik instance yours is in Postgres: the user records, the password hashes, the MFA enrolments, the flows and stages you customised, the policies, the provider configuration including signing keys, and the tokens other systems hold. Lose that database and you have not lost a service, you have lost every login in your organisation and every enrolled second factor, and the recovery is not a restore — it is re-enrolling every human being.
Be aware that authentik's own guidance on this has changed over time: the project has moved away from shipping a built-in backup mechanism and toward telling you, correctly, that backing up PostgreSQL is a PostgreSQL problem with mature solutions. Verify the current position in their docs. Either way, the operational answer is the same, and it has three parts. Take regular logical or physical backups. Store them somewhere that is not the same machine and not the same failure domain. And restore one, into a scratch instance, on a schedule — because an untested backup is a belief, not a backup, and this is not the database you want to discover your beliefs about.
Two extras that belong in the same drawer as the database dump. The secret key, which signs cookies and tokens: keep it in your secret store, because rotating it invalidates sessions and losing it while keeping the database gives you a working install that has forgotten every session. And your certificates and signing keypairs, which live in the database but which relying parties have pinned copies of.
2. The URL becomes immutable the moment anything integrates
This one has teeth, and it is specific to identity providers in a way that catches people who have deployed a hundred web apps without incident.
When a relying party integrates with an OIDC provider, the issuer URL is not a convenience — it is part of the contract. The issuer is written into every token as a claim, it is what the client validates on every verification, and it is the base for discovery and for the JWKS endpoint the client fetches keys from. In authentik, that issuer is derived from your external URL and the application's slug, which means both the hostname and the slug are load-bearing. SAML is the same story with different nouns: the entity ID and the ACS and SSO URLs are pasted into the other side's configuration, sometimes by a partner's IT department who will not enjoy being asked to change them.
So decide the hostname once, before you integrate the first application, and pick something boring and permanent. Put it on a domain you will still own in five years. Do not use the platform-generated hostname of whatever you deployed it on as your permanent identity URL — use a custom domain from the start, so the DNS record is the thing that moves when the infrastructure does. Terminate TLS properly and keep it valid, because unlike a web app that degrades to a browser warning a user can click through, an OIDC client validating your discovery document over a bad certificate simply refuses, machine-to-machine, with an error message that names TLS about a third of the time.
And configure authentik to know its own public URL, rather than inferring it from whatever the proxy in front happened to forward. Getting this wrong produces the classic identity-provider bug: a login that redirects in a circle, or a token whose issuer claim does not match what the resource server expects, and a team that spends the afternoon looking at the client configuration, which is fine.
3. You will eventually lock yourself out
It happens through ordinary competence, not carelessness. You bind a policy that turns out to exclude you. You enforce MFA and then lose the phone. You edit the default authentication flow and remove the stage that was doing something you did not realise it was doing. You federate the admin account to an upstream identity source and then the upstream has an outage. Every one of these leaves you staring at your own login page, unable to reach the admin interface that would let you undo it.
authentik anticipates this. There is a recovery mechanism you run from a shell on the server — conceptually, a management command that mints a single-use, time-limited recovery link for a given user, which you paste into a browser to get back into the account without going through the flow you just broke. The command name is in the family of ak create_recovery_key; check the current documentation for the exact spelling and argument order for your version, because this is precisely the command you do not want to be guessing at during an outage.
Three things follow from that. First, you must retain a way to get a shell where the application runs — if your deployment model has no exec path, your recovery path is a redeploy. Second, rehearse it: run the command once on a staging instance, today, while nothing is on fire, and write the output into your runbook. Third, keep a break-glass local account that does not depend on any external identity source, with credentials in a physical or offline store, excluded from the policies you are about to write. The whole point of a break-glass account is that it survives the change that broke everything else.
4. Upgrades run schema migrations
authentik releases frequently on a date-based scheme, and upgrades apply database migrations on startup. That is the right design — it means there is no separate migration dance — but it has consequences worth internalising.
A migration that fails halfway leaves you with a database that neither the old version nor the new version is entirely happy with. Rolling back the container image does not roll back the schema. And because authentik moves quickly, teams that pin a version and forget about it for a year do not get a one-step upgrade later; they get a sequence of them, each with its own release notes describing the breaking change they skipped.
- Pin an exact release tag. Never track a floating latest tag for the service that gates every other service — an unattended image pull should not be able to migrate your identity schema at three in the morning.
- Snapshot or dump the database immediately before every upgrade, and confirm the dump completed before you start the new container. This is your only rollback.
- Read the release notes for every version between yours and the target, not just the target's. The thing that breaks is in a release you skipped.
- Upgrade on a cadence you can sustain — monthly is comfortable — so each step is small and the notes are short.
- Rehearse against a clone of production data, so the real upgrade is a repeat rather than an experiment. If your Postgres supports cloning a database to a new one, this stops being a chore and starts being a two-minute step.
- Do it during a window when your team can log in by other means, and check that your break-glass path still works after the upgrade, not before.
The deployment, concretely
Here is the shape of a real deployment: server and worker from the same pinned image, Redis local to the deployment, and Postgres somewhere managed with TLS. The double-underscore convention in the environment variables is how authentik nests configuration keys — AUTHENTIK_POSTGRESQL__HOST maps onto the postgresql.host setting — and it is worth knowing because it means you can set anything in the configuration tree from the environment without a config file.
# docker-compose.yml -- authentik: two processes, one image, external Postgres.
# Pin an exact release tag. 'latest' on the service that gates every other
# service is how a schema migration happens while you are asleep.
x-authentik: &authentik
image: ghcr.io/goauthentik/server:2026.x.x # <- a real tag from releases
restart: unless-stopped
env_file: [.env]
depends_on: [redis]
services:
server:
<<: *authentik
command: server
ports:
- "9000:9000" # HTTP; TLS is terminated by the proxy in front
volumes:
- ./media:/media # icons + backgrounds. NOT in Postgres. See below.
- ./custom-templates:/templates
worker:
<<: *authentik
command: worker
# Same image, same env, no ports. Scheduled tasks, blueprints, outpost
# reconciliation, email. Omit this and everything asynchronous silently
# stops -- including the password reset somebody is waiting on right now.
volumes:
- ./media:/media
- ./certs:/certs
redis:
image: redis:alpine
restart: unless-stopped
command: --save 60 1 --loglevel warning
# Cache, task broker, channel layer. Losing this logs people out.
# It does not lose identities -- that distinction is the whole reason
# Postgres lives somewhere with real backups and this does not.And the environment. The secret key is the one value here that is not recoverable from anywhere else, so it belongs in your secret store before it belongs in a file.
# .env -- generate the secret key ONCE and keep it in your secret store.
# Rotating it invalidates every session; losing it while keeping the database
# gives you an install that works and has forgotten everyone is logged in.
AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')
# --- Postgres: the state of record. TLS, always. ---
AUTHENTIK_POSTGRESQL__HOST=abc123.db.pandastack.ai
AUTHENTIK_POSTGRESQL__PORT=5432
AUTHENTIK_POSTGRESQL__NAME=pandastack
AUTHENTIK_POSTGRESQL__USER=pandastack
AUTHENTIK_POSTGRESQL__PASSWORD=<from your secret store, not from here>
AUTHENTIK_POSTGRESQL__SSLMODE=require
# --- Redis: cache, queue, channels. Local to the deployment is fine. ---
AUTHENTIK_REDIS__HOST=redis
AUTHENTIK_REDIS__PORT=6379
AUTHENTIK_REDIS__PASSWORD=
# --- Identity of the deployment. Decide this ONCE. See the section above:
# the issuer URL ends up inside every token every relying party validates.
AUTHENTIK_LISTEN__HTTP=0.0.0.0:9000
AUTHENTIK_COOKIE_DOMAIN=id.example.com
# --- Email. Without it, password recovery and invitations do not exist. ---
AUTHENTIK_EMAIL__HOST=smtp.example.com
AUTHENTIK_EMAIL__PORT=587
AUTHENTIK_EMAIL__USERNAME=authentik@example.com
AUTHENTIK_EMAIL__PASSWORD=<secret store>
AUTHENTIK_EMAIL__USE_TLS=true
AUTHENTIK_EMAIL__FROM=authentik@example.com
# --- Housekeeping ---
AUTHENTIK_ERROR_REPORTING__ENABLED=false
AUTHENTIK_LOG_LEVEL=info
# --- First boot only. Creates the initial admin so you are not doing the
# setup wizard by hand. Remove these once a real admin account exists.
AUTHENTIK_BOOTSTRAP_EMAIL=admin@example.com
AUTHENTIK_BOOTSTRAP_PASSWORD=<long random, rotate after first login>
AUTHENTIK_BOOTSTRAP_TOKEN=<long random API token for automation>
# Verify variable names against the current authentik docs -- the nesting
# convention is stable but individual keys have moved between releases.Once it is up, the configuration itself can live in git rather than in someone's browser history. authentik has blueprints: declarative YAML documents, applied by the worker, that describe flows, stages, policies, providers, applications and their bindings. This is the answer to "a login flow built by clicking exists in exactly one place," and it is the difference between a deployment you can rebuild and a deployment you can only restore. Start with the parts that matter — your providers, your application access policies, your custom flows — and leave the defaults alone.
Outposts, and putting a login in front of things that have none
The feature that makes authentik attractive for homelabs and internal tooling alike is that it can protect applications that have no concept of authentication. There are two ways it does this, and the distinction matters for where you deploy things.
In proxy mode, the outpost is the reverse proxy: traffic hits it, it checks for a valid session, sends the user to authentik if not, and forwards to the upstream application once satisfied, optionally injecting headers describing the user. In forward-auth mode, your existing reverse proxy stays in the path and asks the outpost for a verdict on each request — nginx does this with an auth subrequest, Traefik and Caddy have their own equivalents. Forward auth is usually the right choice if you already run an ingress you like, because it keeps one thing in the request path instead of two.
The embedded outpost inside the server process handles this for small deployments with nothing extra to run. External outposts exist for when you want the enforcement point next to the application rather than next to authentik — a different network, a different region, a different security zone. They connect outbound to the authentik core, which means the outpost needs to reach authentik but authentik never needs to reach the outpost. That property is what makes the model work across network boundaries you do not control.
# An external proxy outpost, deployed next to the app it protects.
# Create the outpost in authentik first; it gives you a token.
services:
outpost:
image: ghcr.io/goauthentik/proxy:2026.x.x # match your core version
restart: unless-stopped
ports:
- "9000:9000"
environment:
# Where the core lives. This is the PUBLIC url -- the outpost also
# redirects browsers here, so an internal-only address breaks login.
AUTHENTIK_HOST: https://id.example.com
AUTHENTIK_TOKEN: ${OUTPOST_TOKEN}
AUTHENTIK_INSECURE: "false"
# Forward-auth with nginx instead: keep your ingress in the path and ask
# the outpost for a verdict per request.
#
# location /outpost.goauthentik.io/ {
# proxy_pass http://outpost:9000/outpost.goauthentik.io/;
# proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
# }
# 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;
# proxy_pass http://your-app:8080;
# }
#
# Check the current directives in the authentik docs before pasting these:
# the exact paths and header names have changed across releases.Running it on PandaStack
This is my product, so read the following as an interested party's description. It maps well onto authentik for a specific reason, and it has one constraint I am going to be blunt about.
A PandaStack app is a full Ubuntu 24.04 userspace inside a Firecracker microVM, deployed from a git repository, with hardware KVM isolation and its own kernel rather than a shared container runtime. For authentik that matters because you are running two long-lived processes plus a local Redis, and you want a real process tree, real memory and root inside your own machine — which is what a microVM is, as opposed to a shared kernel with namespaces drawn on it. Deploys are blue-green: a new machine is built, health-checked, and traffic is flipped, which is the correct shape for a service that must not be half-migrated in public.
The dependency that actually matters is Postgres, and that is a managed product: one dedicated Firecracker microVM per database with a durable volume, a TLS connection string, point-in-time restore, and clone-to-a-new-database — which is exactly the primitive that turns "rehearse the upgrade against production data" from a chore into a step. Creation takes 30 to 90 seconds. You attach it with environment variables, which is the shape authentik's configuration already wants.
# 1. The database first. This is the part you must not lose, so it is the
# part somebody else backs up. 30-90s to ready, TLS on by default.
pandastack db create --label authentik-db --size 4g
pandastack db get authentik-db --json | jq -r .connection_url
# postgres://pandastack:...@abc123.db.pandastack.ai:5432/pandastack?sslmode=require
# 2. The app, from a small deploy repo that starts the server and the worker
# (and a local Redis) inside the VM. Redis holds sessions and queues, not
# identities -- that is why it can live here and Postgres cannot.
pandastack apps create authentik \
--git-url https://github.com/acme/authentik-deploy \
--branch main \
--env AUTHENTIK_SECRET_KEY="$(openssl rand -base64 60 | tr -d '\n')" \
--env AUTHENTIK_POSTGRESQL__HOST=abc123.db.pandastack.ai \
--env AUTHENTIK_POSTGRESQL__NAME=pandastack \
--env AUTHENTIK_POSTGRESQL__USER=pandastack \
--env AUTHENTIK_POSTGRESQL__PASSWORD="$PGPASSWORD" \
--env AUTHENTIK_POSTGRESQL__SSLMODE=require \
--env AUTHENTIK_REDIS__HOST=127.0.0.1 \
--env AUTHENTIK_ERROR_REPORTING__ENABLED=false
# 3. The hostname. Do this BEFORE you integrate anything -- the issuer URL
# ends up baked into every relying party's configuration.
pandastack apps domains add authentik --domain id.example.com
# 4. Keep it awake. An identity provider is not a scale-to-zero workload.
pandastack apps update authentik --scale-to-zero false --health-path /-/health/live/
pandastack apps deploy authentik --follow
# 5. Rehearse the recovery path TODAY, not during the outage. Exact command
# name and arguments: check the current authentik docs for your version.
pandastack apps exec authentik -- ak create_recovery_key 10 akadmin
# -> paste the one-time URL into a browser to get back in when the flow
# you just edited has locked you out of your own admin interface.Now the honest constraints, in the order they will bite you.
We do not offer managed Redis. For authentik that is acceptable — run it inside the same VM, because Redis here is cache, queue and session state, and the blast radius of losing it is "everyone logs in again," not "everyone re-enrols their security key." But be clear-eyed that it is state on an ephemeral machine, and that a deploy will therefore log people out. If that is unacceptable for your users, point authentik at a Redis you run elsewhere.
Media is the other local-state trap, as described earlier: icons and backgrounds uploaded through the admin interface live on the machine's filesystem, and blue-green deploys give you a new machine. Bake assets into the deploy repository or use object storage. And guest memory comes from the template tier at snapshot-restore time rather than being resized on a running instance, so choose the tier deliberately up front — authentik is a Python application with a worker beside it, not a 128MB function.
Then the feature I am telling you not to use. PandaStack apps can scale to zero: idle apps hibernate and cost nothing, and a request wakes them. It is one of the best things about the platform for an internal dashboard nobody looks at on weekends. It is the wrong setting for an identity provider, and I would rather say that plainly than sell you a number. Every login in your organisation passes through this service, including the machine-to-machine token requests that have short timeouts and no patience, and a cold path in that position converts a platform feature into user-visible latency on the most sensitive request in your stack — and into occasional timeout errors downstream that will be diagnosed as anything except the identity provider waking up. Pin it always-on. Spend the scale-to-zero savings on the applications sitting behind it, which is where that feature is genuinely excellent: preview environments, internal tools, staging copies, everything that is idle most of the week.
And where we are the wrong answer: we do not offer managed authentik. We host the app, the database it needs and the hostname it answers on. The upgrades, the flow configuration, the CVE response and the 3am page are still yours. If you wanted somebody else to own those, the last row of the comparison below is the honest recommendation.
authentik, Keycloak, a hosted IdP, or just forward auth
Qualitatively, and with the standard caveat: all four of these move, so verify feature coverage against current documentation before you commit. I have deliberately not quoted anyone's pricing or performance.
- authentik — Model: Python and Django with Go proxy components; server, worker, Postgres, Redis, plus outposts you can deploy near the apps they protect. Strengths: the flow/stage/policy model is unusually flexible, the proxy and forward-auth story is first-class, and it protects applications that have no auth of their own. Trade: an abstraction you have to learn, frequent releases with schema migrations, and configuration that lives in a database unless you adopt blueprints. Best for: teams protecting a mixed estate of modern apps and legacy internal tools with one identity layer.
- Keycloak — Model: a JVM application on Quarkus with an external RDBMS, an operator for Kubernetes, and realm export/import as the config-as-code path. Strengths: enormous protocol maturity, a commercially supported distribution, a large ecosystem and a deep hiring pool. Trade: JVM memory sizing, historically disruptive major versions, and hostname and proxy-header configuration that produces the classic redirect loop. Best for: enterprises that need a support contract or already run the JVM competently.
- A hosted IdP (Auth0, Okta and that class) — Model: somebody else runs it, you configure it. Strengths: no upgrades, no migrations, no backups of the identity database, compliance paperwork already done, and an availability guarantee that is contractual rather than aspirational. Trade: per-user or per-connection pricing that grows with success, less protocol-level control, and your login path depending on a vendor's status page. Best for: teams where identity is not the product and the bill is smaller than a fraction of an engineer.
- Forward auth only (oauth2-proxy, Authelia, your proxy's own module) — Model: a thin authentication layer in front of internal services, usually federating to an existing identity source rather than being one. Strengths: dramatically less to run, often a single binary and a config file, and it solves the actual problem when the actual problem is "put a login in front of six internal dashboards." Trade: it is not an identity provider — no user directory of record, no SAML for the vendor asking for it, no enrollment flows, no application library. Best for: internal-tools gating where a Google or GitHub organisation is already the source of truth.
- No SSO at all — Model: per-application accounts and a password manager. Strengths: nothing to run, nothing to upgrade, no single point of failure. Trade: no central offboarding, which is the security control single sign-on actually buys you. Best for: very small teams, right up until the first person leaves.
When not to self-host this
I would rather you make this decision with the costs visible, so here is the case against, made properly.
If you are five people without an on-call rotation, a hosted identity provider is the better engineering decision, and it is not close. The self-hosting argument is usually cost, and cost is the weakest of the three good reasons, because self-hosting does not delete a cost — it converts a predictable invoice into a machine, a database, an upgrade cadence, a backup regime and somebody's attention. The honest comparison is not "a bill versus free." It is "a bill versus a VM plus the hours of whoever gets paged."
And be specific about what that page looks like, because an identity outage is genuinely worse than the outages you are used to. Nobody can log into anything. Your on-call engineer cannot log into the monitoring, the ticketing system or the chat where the incident is being coordinated. If the runbook is behind SSO, the runbook is gone. If the admin account uses MFA and the MFA state is in the database you are trying to restore, you are in a loop. Two in the morning is not the moment to discover which of your tools has a local account and which does not.
The reasons that do hold up are requirements rather than optimisations, and each one licenses the operational weight on its own. Data residency or a contractual obligation that credentials never leave infrastructure you control. An air-gapped or egress-restricted network where no hosted provider can be reached. A per-user or per-enterprise-connection bill that has become a genuine line item at your scale. Protocol-level control that hosted products do not expose — an LDAP endpoint for the twenty-year-old appliance, a RADIUS server for the VPN, a login page in front of software that has never heard of OAuth. authentik is unusually strong on that last one, and it is the reason a lot of people choose it over the alternatives.
If you land on self-hosting, the mitigations are small and they are all things you do before the incident, not during it: a break-glass local account outside your policies, with credentials offline; the recovery command rehearsed and written into a runbook that does not live behind the login; database backups with a restore you have actually performed; a pinned version and a monthly upgrade slot; and a second, unrelated way for your on-call to reach the machine.
The short version
authentik is four components and a fifth that lives elsewhere: a server, a worker that is more important than its name suggests, PostgreSQL holding every identity you have, Redis holding sessions you can afford to lose, and outposts enforcing decisions next to the applications they protect. Learn the flow, stage, policy, provider and application model before you fight the admin interface, because everything you will want to do is expressed in those five nouns.
Then make four promises and keep them. Back up the identity database and restore it somewhere on a schedule. Choose the hostname once, on a domain you will own for years, before the first relying party integrates. Rehearse the recovery path while nothing is broken, and keep a break-glass account outside every policy you write. Pin a version and upgrade on a cadence, because migrations run at startup and rolling an image back does not roll a schema back.
And if that list reads as a lot for the size of your team, take the hosted option and spend the time on your product. The best identity provider is the one that is up, and knowing which category you are in is worth more than any feature comparison.
Self-hosting your identity provider means accepting that one day the service you cannot log into is the one that decides whether you can log in. Everything in the runbook flows from that sentence.
Frequently asked questions
What do you actually need to run authentik?
Four things, and a fifth that is optional but common. The server process serves the admin interface, the login flows and the protocol endpoints. The worker process runs scheduled tasks, applies blueprints, reconciles outposts and sends email — it is not optional, and a deployment missing it boots cleanly while quietly failing to do anything asynchronous. PostgreSQL is the state of record holding users, credentials, MFA enrolments, flows, policies, providers and certificates. Redis provides cache, the task queue and the channel layer, and losing it logs people out without losing identities. Optionally you also run outposts: separate proxy, LDAP or RADIUS processes that enforce authentik's decisions next to the applications they protect, connecting outbound to the core so they work across networks you do not control.
What is the difference between a provider and an application in authentik?
A provider is the protocol implementation — an OAuth2/OIDC provider with its client ID, secret, redirect URIs and signing key; a SAML provider with its entity ID and ACS URL; an LDAP or proxy provider that an outpost turns into a listening service. It is how the relying party talks to authentik. An application is what a human sees: a name, an icon, a slug, a launch URL, and a wrapper around exactly one provider. Critically, the application is also where access control lives, because policy bindings on it decide who may authenticate and who sees the tile in their library. You need both. A provider with no application is a functioning set of protocol endpoints that nobody is authorized to use, which is the confusing first hour of most people's first deployment.
What happens if I lock myself out of authentik?
It is a common enough outcome that authentik ships an escape hatch. Conceptually, you get a shell where the application runs and execute a management command that mints a single-use, time-limited recovery link for a given user, then paste that URL into a browser to reach the account without going through the flow you just broke. The command lives in the ak create_recovery_key family, but check your version's documentation for the exact spelling and arguments — this is not a command to guess at during an outage. Three preconditions make it work: your deployment must have an exec path to get a shell, you should rehearse the command on staging before you need it, and you should keep a break-glass local account that does not depend on any external identity source, with credentials stored offline and excluded from the policies you write.
Can I change the URL of my authentik instance later?
Technically yes, practically no, and this is the mistake that is most expensive to fix. When a relying party integrates over OIDC, the issuer URL is written into every token as a claim and validated on every verification, and it is the base for the discovery document and the JWKS endpoint the client fetches signing keys from. In authentik the issuer derives from your external URL and the application slug, so both are load-bearing. SAML has the same property through entity IDs and ACS URLs, which are often pasted into a partner's configuration by someone who will not enjoy being asked to change them. Choose a boring, permanent hostname on a domain you will own for years, use a custom domain rather than any platform-generated hostname, and set it before the first application integrates.
Should I self-host authentik or use a hosted identity provider?
Self-host when you have a requirement rather than an optimisation: data residency or a contract saying credentials never leave your infrastructure, an air-gapped network, a per-user bill that has become a real line item, or a need for protocol-level control that hosted products do not expose — an LDAP endpoint for legacy software, RADIUS for a VPN, or a login in front of an application with no authentication of its own, which is authentik's particular strength. Use a hosted provider when identity is not your product and your team is small. An identity outage is worse than the outages you are used to, because nobody can log into the monitoring, the ticketing system or the chat where you would coordinate the incident, and if the runbook is behind single sign-on then the runbook is gone. If you cannot name who is paged and what they do first, that is your answer.
Can I run authentik on PandaStack?
Yes, with clear boundaries. A PandaStack app is a full Ubuntu 24.04 userspace in a Firecracker microVM with hardware KVM isolation, so the server and worker are just processes with a real kernel underneath, and deploys are blue-green. Managed PostgreSQL 16 gives you the dependency that matters — a dedicated microVM per database with a durable volume, TLS, point-in-time restore and clone-to-a-new-database for rehearsing upgrades — ready in 30 to 90 seconds and attached by environment variable. Three honest caveats: we do not offer managed Redis, so run it inside the same VM and accept that a deploy logs people out; uploaded media lives on the machine's filesystem, so bake assets into the repo or use object storage; and turn scale-to-zero off, because a cold path in the login of every service is user-visible latency in the most sensitive request you have.
Keep reading
- Top 7 Keycloak hosting platforms in 2026 — The other major open-source IdP, and where to run it.
- Scale-to-zero app hosting explained — Why it is excellent for apps and wrong for an identity provider.
- Postgres backup, RPO and RTO explained — The database holding every login deserves a tested restore.
- How to add a custom domain to your app — Set the hostname before the first relying party integrates.
- How to manage environment variables and secrets — Where AUTHENTIK_SECRET_KEY should actually live.
- Managed Postgres 16 — Durable volume, TLS, PITR and clone-to-a-new-database in 30-90s.
49ms p50 cold start. Fork, snapshot, and scale to zero.