stack.basicstack.de/apps/outline/outline.yaml
cto-agent 38c7b8c624 feat(outline): deploy Outline knowledge base at outline.basicstack.de (DEV-559)
- Namespace `outline` with Postgres 16, Redis 7, and Outline 1.10.0
- Stateful pods pinned to Hetzner location `fsn1` (encrypted CSI volumes)
- Pocket ID OIDC integration (group-restricted to outline_admins/outline_members)
- Hetzner Object Storage bucket `outline-basicstack` (region hel1) with CORS
- Traefik Ingress with cert-manager `letsencrypt-prod` (DNS-01)
- Sealed secrets for postgres, app (SECRET_KEY/UTILS_SECRET/URLs), OIDC, S3
- Argo CD Application `outline` in `apps/app-outline.yaml`

SMTP notifications deferred (see apps/outline/README.md).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:42:21 +00:00

255 lines
6.4 KiB
YAML

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: outline-data
namespace: outline
spec:
accessModes:
- ReadWriteOnce
storageClassName: hcloud-volumes-encrypted
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: outline
namespace: outline
labels:
app: outline
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: outline
template:
metadata:
labels:
app: outline
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: csi.hetzner.cloud/location
operator: In
values:
- fsn1
initContainers:
- name: wait-for-postgres
image: postgres:16-alpine
command:
- sh
- -c
- |
until pg_isready -h outline-postgres -p 5432 -U "$POSTGRES_USER"; do
echo "Waiting for outline-postgres..."; sleep 2;
done
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: outline-postgres-credentials
key: POSTGRES_USER
- name: wait-for-redis
image: redis:7-alpine
command:
- sh
- -c
- |
until redis-cli -h outline-redis -p 6379 ping | grep -q PONG; do
echo "Waiting for outline-redis..."; sleep 2;
done
containers:
- name: outline
image: docker.io/outlinewiki/outline:1.10.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: PORT
value: "3000"
- name: URL
value: "https://outline.basicstack.de"
- name: FORCE_HTTPS
value: "false"
- name: ENABLE_UPDATES
value: "false"
- name: WEB_CONCURRENCY
value: "2"
- name: DEFAULT_LANGUAGE
value: "en_US"
- name: RATE_LIMITER_ENABLED
value: "true"
- name: LOG_LEVEL
value: "info"
- name: FILE_STORAGE
value: "s3"
- name: FILE_STORAGE_UPLOAD_MAX_SIZE
value: "262144000"
- name: PGSSLMODE
value: "disable"
- name: DATABASE_CONNECTION_POOL_MIN
value: "5"
- name: DATABASE_CONNECTION_POOL_MAX
value: "20"
# Secrets
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: outline-app-secrets
key: SECRET_KEY
- name: UTILS_SECRET
valueFrom:
secretKeyRef:
name: outline-app-secrets
key: UTILS_SECRET
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: outline-app-secrets
key: DATABASE_URL
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: outline-app-secrets
key: REDIS_URL
# S3 (Hetzner Object Storage)
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: outline-s3
key: access-key
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: outline-s3
key: secret-key
- name: AWS_REGION
valueFrom:
secretKeyRef:
name: outline-s3
key: region
- name: AWS_S3_UPLOAD_BUCKET_URL
valueFrom:
secretKeyRef:
name: outline-s3
key: endpoint
- name: AWS_S3_UPLOAD_BUCKET_NAME
valueFrom:
secretKeyRef:
name: outline-s3
key: bucket
- name: AWS_S3_FORCE_PATH_STYLE
value: "true"
- name: AWS_S3_ACL
value: "private"
# OIDC (Pocket ID)
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: outline-oidc
key: client-id
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: outline-oidc
key: client-secret
- name: OIDC_AUTH_URI
value: "https://auth.basicstack.de/authorize"
- name: OIDC_TOKEN_URI
value: "https://auth.basicstack.de/api/oidc/token"
- name: OIDC_USERINFO_URI
value: "https://auth.basicstack.de/api/oidc/userinfo"
- name: OIDC_LOGOUT_URI
value: "https://auth.basicstack.de/api/oidc/end-session"
- name: OIDC_USERNAME_CLAIM
value: "email"
- name: OIDC_DISPLAY_NAME
value: "Pocket ID"
- name: OIDC_SCOPES
value: "openid profile email"
- name: OIDC_DISABLE_REDIRECT
value: "false"
# Persist local cache directory
volumeMounts:
- name: outline-data
mountPath: /var/lib/outline/data
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi
livenessProbe:
httpGet:
path: /_health
port: 3000
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 5
readinessProbe:
httpGet:
path: /_health
port: 3000
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
volumes:
- name: outline-data
persistentVolumeClaim:
claimName: outline-data
---
apiVersion: v1
kind: Service
metadata:
name: outline
namespace: outline
labels:
app: outline
spec:
type: ClusterIP
selector:
app: outline
ports:
- name: http
port: 80
targetPort: 3000
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: outline
namespace: outline
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
ingressClassName: traefik
tls:
- hosts:
- outline.basicstack.de
secretName: outline-tls
rules:
- host: outline.basicstack.de
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: outline
port:
number: 80