Learn Authentik - Installing Authentik
Episode 3 of 31

Learn Authentik - Installing Authentik

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.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

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.

Installation Methods

Before touching the keyboard, get to know the three installation paths:

  • Docker Compose (recommended) — assembles all components as containers in a single file. Suitable for homelabs, VPSs, and small-to-medium production. This is what we use in this episode.
  • Kubernetes (Helm chart) — for platforms already running on Kubernetes. Episode 24 covers it.
  • Binary / manual — running Authentik directly on a host without containers. Only for advanced cases; we don't cover it in depth in this series.

Project Structure and docker-compose.yml

Create a project directory, then prepare the docker-compose.yml file. The official documentation even provides a ready-to-use file you can download:

Create a directory and download the official compose (optional)
mkdir authentik && cd authentik
curl -O https://docs.goauthentik.io/compose.yml

Here'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).

docker-compose.yml — the Authentik stack
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:

  • Server and worker use the same image (ghcr.io/goauthentik/server), differentiated only by command: server or command: worker.
  • PostgreSQL stores all persistent data; the healthcheck ensures the server only comes up after the database is ready.
  • Redis stores the cache and message queue; losing its data is fine, losing PostgreSQL isn't.
  • Ports 9000 (HTTP) and 9443 (HTTPS) are mapped to the host; behind a reverse proxy, they don't need to be exposed to the public.
  • Secrets are never written directly in this file — everything is pulled from .env via interpolation.

The .env File for Secrets

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:

.env — never commit this
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=info

The 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_LEVELinfo for day-to-day use; raise it to debug when troubleshooting.

Generate random values with OpenSSL:

Generate random secrets 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.

Running the Stack and Verifying

Pull the images and create the containers:

docker compose pull
docker compose up -d
docker compose ps

Wait 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.

Bootstrap Admin and Web UI

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:

Create a superuser manually
docker compose exec server ak manage createsuperuser

Tip

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.

Post-Installation Configuration

Before production use, complete a few things:

  • Change the default password — the bootstrapped akadmin isn't for long-term use.
  • Configure email (SMTP) — via admin interface → System → Global Email; required for recovery flows and email stages.
  • Domain and TLS — point 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.
  • Backup — at minimum schedule a PostgreSQL backup (pg_dump). Episode 26 covers a comprehensive backup strategy.

The Upgrade Path

Upgrading Authentik is done via Compose: download the latest compose.yml, pull the new images, then restart the stack:

The Authentik upgrade flow
docker compose pull
docker compose up -d

Database 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.

Closing

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:

  • Server and worker share an image; the only difference is the command.
  • PostgreSQL must not be lost; Redis can be rebuilt.
  • Secrets live in .env, not in the compose file, and are never committed.
  • Bootstrap creates akadmin and the built-in flows; change its password immediately.
  • Upgrade = pull + up, but always back up first.

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!

Learn Authentik - Installing Authentik | Learning Authentik