Use case · Identity and SSO

authentik self-hosted on a dedicated server: requirements, Docker Compose, first login

To run authentik self-hosted you need a Linux host with at least 2 CPU cores and 2 GB of RAM, Docker with Compose v2, the project's own compose.yml, and an .env file holding a database password and a secret key. The stack is three containers: server, worker and PostgreSQL. The part that needs care is the first visit: whoever opens /if/flow/initial-setup/ first sets the akadmin password, so that visit happens through an SSH tunnel before the instance has a public name. Below: the official figures and their scope, the install with both web ports held on loopback, the reverse proxy headers authentik requires, and what to back up.

See the machines

What the stack runs

authentik is an open-source identity provider. Applications sign users in against it over OAuth2/OpenID Connect, SAML, LDAP, RADIUS or a proxy provider, and every login is a flow: a sequence of stages (identification, password, login) you can rearrange in the admin interface. The official Compose file starts three services:

ContainerJobPersistent path
serverCore API, flow execution, SSO endpoints, and the embedded outpost for proxy providers./data (uploaded icons, flow backgrounds, CSV reports), ./custom-templates
workerBackground tasks: email, event notifications, outpost deployment through the Docker socket./data, ./certs, ./custom-templates
postgresqlAll configuration and user data, except uploaded filesnamed volume database

LDAP, RADIUS, proxy and RAC providers run in an outpost, a separate service that talks to the core over the API and websockets. On Docker Compose the worker creates outposts itself through /var/run/docker.sock, which is why the Compose file mounts the socket into the worker (the file also runs the worker as root).

Requirements and sizing

The installation guide asks for a host with at least 2 CPU cores and 2 GB of RAM, plus Docker Compose v2 or Podman. That page covers the Compose method, which the docs describe as suited to test setups and small-scale production; they also cover a Kubernetes install and a high-availability guide. The docs give no per-user or per-login figure, so anything above the minimum is your own estimate.

Our reading: memory goes first, because PostgreSQL caches its working set in RAM and the Compose file sets shm_size: 512mb on both the server and the worker, a ceiling on their shared memory rather than a fixed reservation. Cores matter when many logins land at once. Disk needs room for the images, the database, and the dumps you retain, one of them taken before every upgrade.

On the dedicated server range, a Moonshot cartridge with 8 cores and 8 to 32 GB clears the minimum several times over for a team-sized directory and keeps the identity service off the machines it protects. A racked single-socket server suits an instance that also hosts the database for other apps, and it adds a full out-of-band console. Racked machines are handed over in about 30 minutes once payment is approved.

Install with the ports on loopback

Docker's -p-style port publishing writes its own iptables rules, so ufw does not filter it, and an unqualified mapping listens on IPv4 and IPv6 alike. Set ufw to allow SSH only before the first start, then hold authentik's two listeners on 127.0.0.1. The COMPOSE_PORT_HTTP and COMPOSE_PORT_HTTPS variables are the docs' way to change the published ports; giving them an address prefix is our use of them, and it leaves nothing bound on [::].

sudo ufw allow OpenSSH && sudo ufw enable
sudo mkdir -p /opt/authentik && cd /opt/authentik
sudo wget https://docs.goauthentik.io/compose.yml
sudo touch .env && sudo chmod 600 .env
echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" | sudo tee -a .env >/dev/null
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')" | sudo tee -a .env >/dev/null
echo "COMPOSE_PORT_HTTP=127.0.0.1:9000" | sudo tee -a .env >/dev/null
echo "COMPOSE_PORT_HTTPS=127.0.0.1:9443" | sudo tee -a .env >/dev/null
sudo chmod 600 .env
sudo docker compose pull && sudo docker compose up -d
sudo ss -tlnp | grep -E ':(9000|9443) '

The two openssl rand lines are the documented way to generate the database password and the secret key; the docs note that PostgreSQL does not support passwords longer than 99 characters, and 36 random bytes come out as 48 in base64. Creating .env with mode 600 before writing to it, and setting it again after, keeps both secrets readable by root alone. The last command should list 127.0.0.1 only. Leave /etc/localtime unmounted: the docs warn that authentik runs on UTC internally and a mounted timezone breaks OAuth and SAML.

Claiming akadmin privately

A fresh instance has an akadmin account with no password. The initial-setup flow asks the first visitor to set one, and nothing checks who that visitor is. Open it over an SSH tunnel under your own login, before any DNS name or proxy points at the box.

  1. From your workstation: ssh -L 9000:127.0.0.1:9000 you@your-server.
  2. Open http://localhost:9000/if/flow/initial-setup/ and set the email and password for akadmin.
  3. Sign in and add a named admin account for yourself before anyone else can reach the login page.
  4. If the flow will not load, the troubleshooting page says to restart the containers first; if that fails, docker compose exec server ak changepassword akadmin sets the password directly, as a last resort.

Reverse proxy and TLS

authentik needs a proxy that speaks HTTP/1.1 or newer, because outposts hold websocket connections, and it must forward Host, X-Forwarded-Proto, X-Forwarded-For and the upgrade headers. It honours forwarded headers only from trusted networks; the defaults include 127.0.0.0/8 and the private ranges Docker's bridges draw from, so nginx on the same host needs no change to AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS. Adapted from the docs' nginx example, pointed at the loopback HTTP listener:

map $http_upgrade $connection_upgrade_keepalive {
    default upgrade;
    ''      '';
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name sso.example.com;
    ssl_certificate     /etc/letsencrypt/live/sso.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sso.example.com/privkey.pem;
    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade_keepalive;
    }
}

Then sudo ufw allow 80,443/tcp. An endless spinner or mixed-content errors after this step usually mean X-Forwarded-Proto is not reaching authentik as https.

Backups and upgrades

The backup guide calls PostgreSQL the most important part: users, flows, policies and configuration all live there. Dump it with native tools and copy the result off the host; tuning the database itself is covered on our PostgreSQL dedicated server page. From a cron script, use exec -T so Compose does not ask for a terminal:

cd /opt/authentik
docker compose exec -T postgresql pg_dump -U authentik -d authentik -cC > /var/backups/authentik-$(date +%F).sql
tar czf /var/backups/authentik-files-$(date +%F).tgz data certs custom-templates .env
chmod 600 /var/backups/authentik-*

If you mount custom blueprints into /blueprints, add that folder to the archive; the backup guide recommends it. The stock compose.yml does not mount it. The archive holds .env, so it carries both secrets; keep it private. Changing AUTHENTIK_SECRET_KEY ends every active session. To upgrade, download the new compose.yml, which pins a fixed release, then docker compose pull and up -d. authentik cannot be downgraded, so dump the database first, never skip a major version (take each one to its newest .x release before moving on), and upgrade outposts at the same time: their version must match the server.

Where this setup stops

One Compose host is one point of failure for every application that signs in through it. If the identity service must survive a machine loss, follow the high-availability guide or Kubernetes instead. The docs flag the worker's Docker socket mount as a security risk: put a socket proxy in front of it, or remove the mount and deploy outposts by hand. If you are weighing Keycloak instead, our self-hosted Keycloak page covers its production mode on one machine.

Questions

What hardware does authentik need?

The Docker Compose guide sets the floor at 2 CPU cores and 2 GB of RAM, and positions that method for tests and small-scale production. It publishes no per-user sizing. Memory headroom for PostgreSQL is the first thing worth adding above the minimum.

Can I run authentik without Docker?

The documented paths are Docker Compose (or Podman), Kubernetes with the Helm chart, and AWS CloudFormation. Everything on this page follows the Compose route, which the docs describe as suited to test setups and small-scale production.

Is the initial setup page dangerous to leave open?

Yes, until it has been used. The first person to complete /if/flow/initial-setup/ sets the akadmin password. Keep ports 9000 and 9443 on loopback and finish the flow over an SSH tunnel before a proxy publishes the instance.

Does authentik still need Redis?

The current compose.yml defines only postgresql, server and worker, so there is no Redis container to run or back up. If you upgrade an older install, read the release notes for every version you pass through.

What do I lose if I lose the database?

Everything that matters: the backup guide says the PostgreSQL database holds users, policies, flows and configuration, and losing it means full application data loss. Uploaded files in ./data and custom templates are restorable separately.

Can the LDAP outpost run on the same machine?

Yes. On Docker Compose the worker can deploy outposts locally through the Docker socket integration, or you can run them yourself from their own Compose file. Each outpost has to match the server's release.

Put the identity provider on its own machine

Whole servers with root, an unmetered port and month-to-month terms, sized from a cartridge to a four-socket box.

See the machines

Related on Server Room

Other use cases