Installing Authentik with Docker Compose: assembling the server, worker, PostgreSQL, Redis, and proxy stack; managing secrets and environment variables through the .env file; bootstrapping the akadmin admin; verifying the first UI; and a safe upgrade path.

After understanding the architecture in episode 2 — server, worker, PostgreSQL, Redis, and outposts — in this episode we'll bring it to life for the first time. The end goal of this episode is simple: Authentik is running, and you can log into the admin interface as akadmin.
We'll use the most recommended method: Docker Compose. This is the pattern used by the official documentation, the easiest to manage, and the foundation for all subsequent episodes.
Before touching the keyboard, get to know the three installation paths:
Create a project directory, then prepare the docker-compose.yml file. The official documentation even provides a ready-to-use file you can download:
mkdir authentik && cd authentik
curl -O https://docs.goauthentik.io/compose.ymlHere's the version we discuss, covering five services: postgresql, redis, server, worker, and proxy. Note: the latest official quickstart runs the proxy outpost embedded in the server; the proxy service below is a standalone outpost option — useful when you want the outpost running as a separate container (covered in phase 4).
services:
postgresql:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${PG_PASS:?database password required}
POSTGRES_USER: ${PG_USER:-authentik}
POSTGRES_DB: ${PG_DB:-authentik}
volumes:
- database:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -d ${PG_DB} -U ${PG_USER}"]
start_period: 20s
interval: 30s
retries: 5
redis:
image: docker.io/library/redis:alpine
restart: unless-stopped
command: --save 60 1 --loglevel warning
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2.6}
command: server
restart: unless-stopped
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:?database password required}
ports:
- "${COMPOSE_PORT_HTTP:-9000}:9000"
- "${COMPOSE_PORT_HTTPS:-9443}:9443"
volumes:
- ./media:/media
- ./custom-templates:/templates
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2.6}
command: worker
restart: unless-stopped
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:?database password required}
volumes:
- ./media:/media
- ./certs:/certs
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
proxy:
image: ghcr.io/goauthentik/proxy:${AUTHENTIK_TAG:-2026.2.6}
restart: unless-stopped
environment:
AUTHENTIK_HOST: http://server:9000
AUTHENTIK_TOKEN: ${AUTHENTIK_BOOTSTRAP_TOKEN}
depends_on:
- server
volumes:
database:
redis:Key points about this file:
ghcr.io/goauthentik/server), differentiated only by command: server or command: worker..env via interpolation.Never put secrets directly in docker-compose.yml. Store them in .env, make sure it's in .gitignore, and don't commit it. Prepare its contents:
PG_PASS=<postgres-password-acak>
PG_USER=authentik
PG_DB=authentik
AUTHENTIK_SECRET_KEY=<secret-acak>
AUTHENTIK_BOOTSTRAP_PASSWORD=<password-admin-kuat>
AUTHENTIK_BOOTSTRAP_EMAIL=admin@example.com
AUTHENTIK_BOOTSTRAP_TOKEN=<token-outpost-acak>
AUTHENTIK_ERROR_REPORTING=false
AUTHENTIK_LOG_LEVEL=infoThe meaning of each important variable:
AUTHENTIK_SECRET_KEY — the key that signs sensitive data; it must be random and long. Changing it after installation will invalidate existing sessions.AUTHENTIK_BOOTSTRAP_PASSWORD and AUTHENTIK_BOOTSTRAP_EMAIL — used to create the first admin named akadmin.AUTHENTIK_BOOTSTRAP_TOKEN — the token used by outposts to communicate with the API.AUTHENTIK_ERROR_REPORTING — turn off (false) for privacy; if left true, errors are sent to the Authentik team.AUTHENTIK_LOG_LEVEL — info for day-to-day use; raise it to debug when troubleshooting.Generate random values with OpenSSL:
openssl rand -base64 60 | tr -d '\n'
openssl rand -base64 36 | tr -d '\n'Warning
If AUTHENTIK_SECRET_KEY or PG_PASS isn't filled in, the server refuses to start — this is built-in protection, not a bug. Always use random secrets, store them somewhere safe, and add .env to .gitignore from the start.
Pull the images and create the containers:
docker compose pull
docker compose up -d
docker compose psWait until the server container is ready, then open a browser at http://localhost:9000 (or the port you mapped). If everything is correct, you'll see the Authentik login page.
On first start, the server automatically creates: the default tenant, the built-in flows (authentication, enrollment, recovery, invalidation), the built-in groups authentik Admins and authentik Users, and the akadmin admin user — complete with the password from AUTHENTIK_BOOTSTRAP_PASSWORD.
Log into the admin interface with akadmin, and you'll be taken to the dashboard. If for some reason the bootstrap didn't run, you can create a superuser manually:
docker compose exec server ak manage createsuperuserTip
After the first login, immediately change the akadmin password through your profile, and if possible enroll an MFA device. An identity provider admin is the most valuable target — treat it with that seriousness. We'll cover MFA in episode 7.
Before production use, complete a few things:
akadmin isn't for long-term use.auth.example.com at the server, place Authentik behind a reverse proxy, and terminate TLS at the proxy. Don't expose ports 9000/9443 directly to the internet without TLS.pg_dump). Episode 26 covers a comprehensive backup strategy.Upgrading Authentik is done via Compose: download the latest compose.yml, pull the new images, then restart the stack:
docker compose pull
docker compose up -dDatabase migrations run automatically when the server starts. But before upgrading: read the release notes, and make sure a recent database backup exists — migrations rarely go backwards.
In this episode 3, you installed Authentik with Docker Compose: five services — postgresql, redis, server, worker, and proxy — managed through a single file, secrets separated in .env with variables like AUTHENTIK_SECRET_KEY, AUTHENTIK_BOOTSTRAP_PASSWORD, and AUTHENTIK_BOOTSTRAP_TOKEN, then the stack was verified and the first admin akadmin logged in successfully.
Key takeaways:
command..env, not in the compose file, and are never committed.akadmin and the built-in flows; change its password immediately.In episode 4, we'll dissect the concept that most distinguishes Authentik: flows & stages — how authentication flows are built from stage blocks, analyzing the built-in flows, creating a custom flow, and debugging with the flow inspector. See you in episode 4!