Outline Knowledgebase Deployment
Objectives
- Set up a collaborative knowledgebase with simple editing and navigation
- For this purpose we are using https://github.com/outline/outline as the knowledgebase
- Other options include docmost, bookstack, or wiki.js
Just give me the files
You can find the final configurations here:
Tools
- Linux base system with docker - Using Debian with Docker
- A way to interact with the system - Using VSCode via SSH
- An Identity Provider to provide authentication (required for outline) - Using PocketID
- A proxy with valid TLS certificates and valid domain - using caddy with let's encrypt http01
- Supporting containers for outline - Using Postgres (database) and Redis (cache)
Process
- Set up the Proxy
- Set up Identity Provider
- Set up Outline
Set up the Reverse Proxy
Info
You can skip this step if you already have a reverse proxy, like caddy or traefik
- Make sure you have your domain setup and DNS (either from your router, domain provider, or both) pointed at your linux server.
- Start by creating the docker network for your proxy to sit on
Info
this guide will be working with folders within /mnt/containers
, and finishing with three compose stacks
- create up a
caddy
folder, and within create acontainer-config
folder and acontainer-data
folder. Incontainer-config
place a text file calledCaddyfile
(note the caps). in the rootcaddy
folder place adocker-compose.yaml
file. docker-compose.yaml
services:
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- 80:80
- 443:443
- 443:443/udp
volumes:
- ./container-config:/etc/caddy
- ./container-data:/data
- /etc/localtime:/etc/localtime:ro
networks:
- reverseproxy-nw
networks:
reverseproxy-nw:
external: true
Caddyfile
auth.<your-domain>.com {
reverse_proxy http://pocketid
}
kb.<your-domain>.com {
reverse_proxy http://outline:8080
}
- Bring up with
docker compose up -d
and verify that you are getting the certificates for the two domains (the sites won't work yet for obvious reasons).
Info
If you want to use let's encrypt certificates but don't want to expose your domain publicly, you can either use DNS challenges (see Reverse Proxies with Caddy), or port forward port 80 only to your server (assuming you are not behind CGNAT). If you do not port forward port 443, port 80 will be used for cert verification but all other external requests will fail.
Set up Identity Provider (PocketID)
Info
You can skip this step if you already have an identity provider like keycloak
Outline does not ship with a built in authentication capability, and relies on an external identity provider (IdP). While this is counterintuitive at first glance, it is in fact best practice to rely on a dedicated identity provider for authentication using Single Sign-On (SSO).
In this circumstance we will set up PocketID as a simple but powerful authentication provider:
- create a pocketid folder in the working directory and create a docker-compose.yaml file within that. Remember to change your domain
docker-compose.yaml
services:
pocketid:
image: ghcr.io/stonith404/pocket-id
container_name: pocketid
restart: unless-stopped
# ports:
# - 3000:80
volumes:
- "./container-data:/app/backend/data"
environment:
- PUBLIC_APP_URL=https://auth.<your-domain>.com
- TRUST_PROXY=true
- PUID=1000
- PGID=1000
networks:
- reverseproxy-nw
networks:
reverseproxy-nw:
external: true
- bring up pocketID with
docker compose up -d
and check that the site works.
Configure PocketID
PocketID is unique in that it only supports passkeys. Historically this would be problematic from a user experience perspective, but nowadays every major device platform (windows, macos, android, ios) supports passkeys natively. Neat! Let's get registered and create a client for outline.
- navigate to
https://auth.<your-domain>.com/login/setup
to perform the first time setup and register your device.
Warning
make sure to change your details before registering your passkey, as changing the username will render the passkey invalid.
Info
you can register more devices by sending one-time links to log in on that device and register it.
- navigate to OIDC Clients and create a client for outline. Set the Callback URLs to
https://kb.<your-domain>.com/auth/oidc.callback
. Copy down the client secret (or regenerate it when adding to outline's configuration).
- (optional) set the session timeout to be higher. 60 min is the default and feels excessively small, I will typically set it either to 1 day or 1 week
Set up Outline
Alright, almost there.
- Create an
outline
folder with adocker-compose.yaml
file and a.env
file. docker-compose.yaml
(it's a long one)
services:
outline:
container_name: outline
image: docker.getoutline.com/outlinewiki/outline:latest
restart: always
# ports:
# - "8080:8080"
volumes:
- ./container-data/outline:/var/lib/outline/data
- /etc/localtime:/etc/localtime:ro
environment:
- PGSSLMODE=disable
- FILE_STORAGE=local
- FORCE_HTTPS=false
- SECRET_KEY=${SECRET_KEY}
- UTILS_SECRET=${UTILS_SECRET}
- DATABASE_URL=postgres://outline:${POSTGRES_PASSWORD}@outline-db:5432/outline
- REDIS_URL=redis://outline-redis:6379
- URL=${WIKI_URL}
- PORT=8080
- OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
- OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
- OIDC_AUTH_URI=${OIDC_AUTH_URI}
- OIDC_TOKEN_URI=${OIDC_TOKEN_URI}
- OIDC_USERINFO_URI=${OIDC_USERINFO_URI}
- OIDC_DISPLAY_NAME=${OIDC_DISPLAY_NAME}
- OIDC_USERNAME_CLAIM=email
depends_on:
- postgres
- redis
networks:
- outline-nw
- reverseproxy-nw
redis:
image: redis
container_name: outline-redis
restart: always
# ports:
# - "6379:6379"
networks:
- outline-nw
postgres:
image: postgres
restart: always
container_name: outline-db
# ports:
# - "5432:5432"
volumes:
- ./container-data/db:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-d", "outline", "-U", "user"]
interval: 30s
timeout: 20s
retries: 3
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_USER=outline
- POSTGRES_DB=outline
networks:
- outline-nw
networks:
outline-nw:
reverseproxy-nw:
external: true
.env
#secrets/passwords. Generated 3x using `openssl rand -hex 32`
SECRET_KEY=<generate a key>
UTILS_SECRET=<generate another key>
POSTGRES_PASSWORD=<generate a third key>
#domains
WIKI_URL=https://kb.<your-domain>.com
#oidc information
OIDC_CLIENT_ID=<from pocket id>
OIDC_CLIENT_SECRET=<from pocket id>
OIDC_AUTH_URI=https://auth.<your-domain>.com/authorize
OIDC_TOKEN_URI=https://auth.<your-domain>.com/api/oidc/token
OIDC_USERINFO_URI=https://auth.<your-domain>.com/api/oidc/userinfo
OIDC_DISPLAY_NAME=Pocket ID
- Create the
container-data/outline
directory and change the owner to the container user (nodejs/1001). Reduce permissions on the.env
file
cd /mnt/containers/outline
mkdir -p container-data/outline
chown 1001:1001 container-data/outline
chmod 600 .env
- Bring up the service (yes you can see secrets in this recording. This is a lab demo that gets wiped).
- Test that pasting content (like images) works correctly
You're good to go!