Outline
A self-hosted team wiki and knowledge base.
Outline is an open-source wiki and documentation platform with a rich Markdown editor, nested document structure, full-text search, and team collaboration features. Self-hosting avoids per-seat subscription costs and keeps internal documentation on your own infrastructure.
Alternatives considered
Cloud Hosted
| Tool | Open Source | Free Tier | Monthly Cost |
|---|---|---|---|
| Notion | No | Limited | From $10/user |
| Confluence | No | Limited | From $6/user |
Self Hosted
| Tool | Open Source | Full Features | Notes |
|---|---|---|---|
| BookStack | Yes | Yes | Simpler wiki with book/chapter structure |
Installation
Architecture
- Deployment: Single
outlinedeployment in theoutlinenamespace - Images:
outlinewiki/outline:1.6.1,valkey/valkey:9.0.3-alpine(both digest-pinned) - Cache: Valkey StatefulSet for session and cache
- Database: CNPG PostgreSQL cluster with Longhorn-encrypted PVCs
- Storage: Longhorn PVC (
outline-data) for file attachments - Networking: ClusterIP service on port 3000, HTTPRoute via internal gateway
Security
- Runs as
runAsUser: 1001,runAsNonRoot: true,readOnlyRootFilesystem: true,allowPrivilegeEscalation: false, capabilities dropped - Longhorn PVCs encrypted at rest
Updates
Managed by Renovate. Both images are digest-pinned.
Data Management
- Database: CNPG PostgreSQL cluster (Longhorn-encrypted PVCs)
- PVC:
outline-data(Longhorn-encrypted,k8up.io/backup: "true") for file attachments - Backups: k8up
Schedulebacks up CNPG Longhorn PVCs to Hetzner S3. CNPG annotated withk8up.io/backupcommand: pg_dump.
User Management
Full OIDC configured — OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_AUTH_URI, OIDC_TOKEN_URI, OIDC_USERINFO_URI, OIDC_LOGOUT_URI, OIDC_USERNAME_CLAIM, and OIDC_SCOPES all set via SOPS-encrypted secret.
Configuration Management
- OIDC credentials and all app secrets from SOPS-encrypted secret
- Database URL injected from CNPG-generated secret
Administration
Usage
Create and organize documents in a nested tree structure. Use Markdown with rich embeds (images, code blocks, tables). Share documents with the team or make them public. OIDC SSO means users authenticate with their existing identity provider credentials.
Cluster-specific deviations from the above live in the per-cluster README — see k8s/apps/talos/outline/README.md.
Cluster Deployment
Outline — Talos cluster
Cluster-specific notes only. General product info, "why we use it", and alternatives live in docusaurus/docs/apps/outline.mdx.
Deviations from defaults
Defaults live in docusaurus/docs/apps/outline.mdx — document anything this cluster does differently here, with a one-line reason.
- Image:
outlinewiki/outline:1.8.1@sha256:e224dcbe34670bdae8835c32d5abc692d3560dfa262b72fb7232f4d87185aebd - Image:
valkey/valkey:9.1.0-alpine@sha256:a35428eba9043cc0b79dbe54100f0c92784f2de00ad09b01182bfb1c5c83d1bd
Rendered manifests (kustomize build)
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
labels:
app: outline
name: outline
namespace: outline
spec:
replicas: 1
selector:
matchLabels:
app: outline
ingress: public
strategy:
rollingUpdate: null
type: Recreate
template:
metadata:
labels:
app: outline
ingress: public
spec:
containers:
- env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
key: uri
name: cnpg-app
envFrom:
- secretRef:
name: outline
image: outlinewiki/outline:1.8.1@sha256:e224dcbe34670bdae8835c32d5abc692d3560dfa262b72fb7232f4d87185aebd
livenessProbe:
failureThreshold: 5
httpGet:
path: /
port: 3000
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
name: outline
ports:
- containerPort: 3000
name: web
protocol: TCP
readinessProbe:
failureThreshold: 5
httpGet:
path: /
port: 3000
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
volumeMounts:
- mountPath: /var/lib/outline/data
name: outline-data
- mountPath: /tmp
name: empty-dir
securityContext:
fsGroup: 1001
fsGroupChangePolicy: OnRootMismatch
runAsGroup: 1001
runAsNonRoot: true
runAsUser: 1001
seccompProfile:
type: RuntimeDefault
volumes:
- emptyDir: {}
name: empty-dir
- name: outline-data
persistentVolumeClaim:
claimName: outline-data
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: valkey
namespace: outline
spec:
replicas: 1
selector:
matchLabels:
app: valkey
serviceName: valkey
template:
metadata:
labels:
app: valkey
spec:
containers:
- args:
- valkey-server
image: valkey/valkey:9.1.0-alpine@sha256:a35428eba9043cc0b79dbe54100f0c92784f2de00ad09b01182bfb1c5c83d1bd
livenessProbe:
initialDelaySeconds: 10
periodSeconds: 10
tcpSocket:
port: 6379
name: valkey
ports:
- containerPort: 6379
name: client
readinessProbe:
initialDelaySeconds: 3
periodSeconds: 5
tcpSocket:
port: 6379
resources:
limits:
memory: 512Mi
requests:
cpu: 50m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
volumeMounts:
- mountPath: /conf
name: conf
- mountPath: /data
name: data
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 999
seccompProfile:
type: RuntimeDefault
volumes:
- emptyDir: {}
name: conf
- emptyDir: {}
name: data