Outline 1.12.0 on this lab answered HTTP 200 at http://127.0.0.1:3030/ after Postgres migrations. Logs showed OIDC plugin registered and Listening on http://127.0.0.1:3030. Dex (lab IdP) served OpenID discovery at http://127.0.0.1:5556/dex and returned HTTP 302 from /dex/auth. Quiet medians: Outline ~323 MiB, Postgres ~31, Redis ~4 (~358 MiB without Dex; ~366 with the lab IdP). There is no local username/password screen. If you do not wire an OIDC provider, you do not get in.
The contrast with BookStack is login: BookStack ships a default admin; Outline assumes Google, Slack, Azure AD, Authentik, Dex, or similar. Production on a VPS usually means Authentik (or another IdP you already run) plus Postgres, Redis, and either local disk or S3-compatible storage for attachments.
| Reading | Value |
|---|---|
| Image | outlinewiki/outline:1.12.0 |
| Database / cache | postgres:16-alpine · redis:7-alpine |
| Lab IdP | dexidp/dex:v2.41.1 |
| App listen | 127.0.0.1:3030 → 3000 |
| Home | HTTP 200 |
| OIDC | plugin registered · Dex issuer OK |
| Idle app+pg+redis | ~358 MiB |
| Idle + Dex | ~366 MiB |
Outline vs BookStack
| Outline | BookStack | |
|---|---|---|
| Login | OIDC (no built-in password) | Local admin (LDAP optional) |
| Shape | Collections / docs (Notion-like) | Books → chapters → pages |
| Quiet RAM (this series) | ~358 MiB | ~146 MiB |
| Files | Local or S3 | App /config + DB |
| Extra services | Redis required | MariaDB only |
Pick Outline when the team already lives in an IdP and wants realtime docs. Pick BookStack when you want a smaller stack and a password on day one.
Compose: Postgres, Redis, Outline, IdP
Lab used Dex so the stack could prove OIDC without standing up Authentik again. Production: point OIDC_* at Authentik (or Google) and drop Dex.
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: outline
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: outline
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline -d outline"]
interval: 5s
timeout: 5s
retries: 12
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 12
outline:
image: outlinewiki/outline:1.12.0
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
env_file: .env
volumes:
- ./data:/var/lib/outline/data
ports:
- "127.0.0.1:3000:3000"
volumes:
pgdata:This lab mapped 3030 because host 3000 was already taken.
SECRET_KEY=$(openssl rand -hex 32)
UTILS_SECRET=$(openssl rand -hex 32)# .env (mode 600) essentials
URL=http://127.0.0.1:3030
DATABASE_URL=postgres://outline:…@postgres:5432/outline
REDIS_URL=redis://redis:6379
SECRET_KEY=…
UTILS_SECRET=…
FILE_STORAGE=local
FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data
FORCE_HTTPS=false
OIDC_CLIENT_ID=outline
OIDC_CLIENT_SECRET=…
OIDC_AUTH_URI=http://127.0.0.1:5556/dex/auth
OIDC_TOKEN_URI=http://dex:5556/dex/token
OIDC_USERINFO_URI=http://dex:5556/dex/userinfo
OIDC_USERNAME_CLAIM=email
OIDC_DISPLAY_NAME=Dex
OIDC_SCOPES=openid profile emailBrowser traffic hits OIDC_AUTH_URI on a host-reachable address. The Outline container hits token/userinfo over compose DNS (dex or your IdP hostname). Mismatch those and login fails after the consent screen.
docker compose up -d
docker logs -f outline-appWait for migrations, then OIDC plugin registered and Listening on….


Prove the app and the IdP
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3030/
# 200
curl -s http://127.0.0.1:5556/dex/.well-known/openid-configuration \
| jq '{issuer, authorization_endpoint}'Lab Dex issuer: http://127.0.0.1:5556/dex. Authorize endpoint returned HTTP 302 for a valid client request.
Register the redirect URI https://wiki.example.com/auth/oidc.callback (path may vary slightly by Outline version; match what Outline documents for your release) on the IdP before inviting humans.

Idle RAM on this lab
| Container | Median MiB |
|---|---|
outline-app | ~323 |
outline-pg | ~31 |
outline-redis | ~4 |
outline-dex (lab only) | ~9 |
| App + DB + Redis | ~358 |
Collaborative editing and uploads sit above this. A Compute Instance with 2 GB is a realistic floor for Outline alone; leave headroom if the IdP shares the box.

Files: local vs S3
FILE_STORAGE=local is enough to learn the UI. For shared durability, use S3-compatible storage (MinIO, Garage, a cloud bucket) with FILE_STORAGE=s3 and the AWS_* / AWS_S3_* variables from Outline’s hosting docs. Path-style access matters for MinIO.
Caddy
wiki.example.com {
reverse_proxy 127.0.0.1:3000
}URL=https://wiki.example.com
FORCE_HTTPS=trueRecreate Outline after changing URL. Leave the app port closed on the panel firewall. TLS pattern matches Caddy HTTPS.

| Skip this | What this lab did |
|---|---|
| Expecting local password login | Wired Dex OIDC |
| Publishing 3000 on all interfaces | 127.0.0.1 only |
latest forever | Pinned 1.12.0 |
| Same hostname for AUTH and TOKEN blindly | Split host vs compose DNS |
| Skipping Redis | Required; healthchecked |
OIDC and file storage are not optional trivia
Outline’s Docker docs treat Postgres, Redis, URL, DATABASE_URL, REDIS_URL, and SECRET_KEY as the minimum. There is no local password login: you wire Google, Slack, Azure, or a generic OIDC provider (OIDC_CLIENT_ID / SECRET plus auth, token, and userinfo URIs) and register https://your-host/auth/oidc.callback. That is why lab installs often sit Dex or Authentik beside Outline; without an IdP the UI never becomes a usable wiki.
Attachments default to FILE_STORAGE=local under /var/lib/outline/data (map a volume). For S3-compatible stores set FILE_STORAGE=s3 with path-style URLs for MinIO (AWS_S3_FORCE_PATH_STYLE=true). Outline’s own compatibility table marks MinIO and several clouds as fine; Cloudflare R2 and Backblaze show up as problem cases in community notes, so test put/get before you cut over. Changing URL after clients exist breaks OIDC redirects the same way Logto’s ENDPOINT does.
FAQ
Can I skip OIDC? Not for a normal self-hosted login. Outline is built around providers.
Slack/Google only? Fine if those IdPs fit your threat model. On-prem teams usually prefer Authentik/Keycloak/Dex.
Enterprise image? Separate commercial image; this lab is community outlinewiki/outline.
Backups? pg_dump plus the file volume or S3 bucket. Off-box with Restic.
What to run on the VPS
Pin Outline 1.12.0, Postgres 16, Redis 7. Generate SECRET_KEY / UTILS_SECRET. Set URL to the final HTTPS hostname before users exist. Point OIDC at Authentik (or Dex for a lab). Keep the app on loopback behind Caddy. A 2 GB+ Compute Instance covers this quiet footprint with room for a small team; size up when collaboration and attachments grow.
