Our Personal Data Server from scratch! tranquil.farm
pds rust database fun oauth atproto
238
fork

Configure Feed

Select the types of activity you want to include in your feed.

adding k8s docs and a more generalized doc #12

open opened by dsx.sh targeting main from dsx.sh/tranquil-pds: k8s-doc-updates

I recently went through some pain in getting Tranquil all set up on my k8s cluster. Luckily some of my 'biggest' pain was in the mystery of the email server, which has been resolved in the meantime.

Regardless, I added a bunch more detail on a somewhat generic k8s deployment, with some context for secrets, secret generation, etc.

I also set up a "general configuration" doc that took some of the details and context from all the install-x docs and compiled it together into a more generalized information source with all the env vars I could find centralized into one place.

Sorry if this kind of work is already being done elsewhere, I wanted to get this somewhere so I can point friends to so they do their own PDS.

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:6so2rykrnjmzebbr2zbwbqbx/sh.tangled.repo.pull/3mlcn4ezr6p22
+238 -11
Diff #3
+211 -7
docs/install-kubernetes.md
··· 1 - # Tranquil PDS on kubernetes 1 + # Tranquil PDS on Kubernetes 2 2 3 - If you're reaching for kubernetes for this app, you're experienced enough to know how to spin up: 3 + If you're reaching for Kubernetes for this app, you're experienced enough to know how to spin up: 4 4 5 5 - cloudnativepg (or your preferred postgres operator) 6 6 - a PersistentVolume for blob storage 7 7 - the app itself (it's just a container with some env vars) 8 8 9 - You'll need a wildcard TLS certificate for `*.your-pds-hostname.example.com`. User handles are served as subdomains. 9 + See `example.toml` for what each config option does and why the secret ones matter. This guide covers the Kubernetes-specific wiring. 10 10 11 - The container image expects: 11 + Minimally, the container image expects: 12 12 - A TOML config file mounted at `/etc/tranquil-pds/config.toml` (or passed via `--config`) 13 13 - `DATABASE_URL` - postgres connection string 14 14 - `BLOB_STORAGE_PATH` - path to blob storage (mount a PV here) ··· 16 16 - `JWT_SECRET`, `DPOP_SECRET`, `MASTER_KEY` - generate with `openssl rand -base64 48` 17 17 - `CRAWLERS` - typically `https://bsky.network` 18 18 19 - and more, check the example.toml for all options. Environment variables can override any TOML value. 20 - You can also point to a config file via the `TRANQUIL_PDS_CONFIG` env var. 19 + --- 21 20 22 - Health check: `GET /xrpc/_health` 21 + ## TLS and DNS 22 + 23 + You need a wildcard TLS cert covering `*.your-pds-hostname.example.com`. User handles are subdomains, so every handle needs a matching SAN. 24 + 25 + An approach using Cert Manager would look something like this: 26 + 27 + ```yaml 28 + apiVersion: cert-manager.io/v1 29 + kind: Certificate 30 + metadata: 31 + name: pds-cert 32 + namespace: pds 33 + spec: 34 + secretName: pds-tls 35 + dnsNames: 36 + - pds.example.com 37 + - "*.pds.example.com" 38 + ``` 39 + 40 + If you're using the apex domain for handles, include it in `dnsNames` alongside the wildcard. 41 + 42 + --- 43 + 44 + ## Secrets 45 + 46 + The three primary key secrets (`JWT_SECRET`, `DPOP_SECRET`, `MASTER_KEY`) must never appear in a manifest or config file. Inject them as a Kubernetes Secret, sourced from wherever you manage secrets. 47 + 48 + To create the Secret directly and manage rotation manually: 49 + 50 + ```bash 51 + kubectl create secret generic pds-secrets -n pds \ 52 + --from-literal=DATABASE_URL='postgres://...' \ 53 + --from-literal=JWT_SECRET="$(openssl rand -base64 48)" \ 54 + --from-literal=DPOP_SECRET="$(openssl rand -base64 48)" \ 55 + --from-literal=MASTER_KEY="$(openssl rand -base64 48)" 56 + ``` 57 + 58 + Then reference it in your Deployment: 59 + 60 + ```yaml 61 + envFrom: 62 + - secretRef: 63 + name: pds-secrets 64 + ``` 65 + 66 + --- 67 + 68 + ## PostgreSQL 69 + 70 + CloudNativePG works well here. Example cluster config: 71 + 72 + ```yaml 73 + apiVersion: postgresql.cnpg.io/v1 74 + kind: Cluster 75 + metadata: 76 + name: postgres 77 + namespace: pds 78 + spec: 79 + instances: 1 80 + bootstrap: 81 + initdb: 82 + database: pds 83 + owner: tranquil_pds 84 + secret: 85 + name: postgres-user-secret # k8s Secret with username/password 86 + storage: 87 + storageClass: your-storage-class 88 + size: 10Gi 89 + ``` 90 + 91 + The `postgres-user-secret` Secret needs `username` and `password` keys. The password you put here is what goes into `DATABASE_URL`. 92 + 93 + Any standard Postgres setup works fine here. 94 + 95 + --- 96 + 97 + ## Storage PVC (if using local-path, nfs, or similar) 98 + 99 + ```yaml 100 + apiVersion: v1 101 + kind: PersistentVolumeClaim 102 + metadata: 103 + name: pds-blobs 104 + namespace: pds 105 + spec: 106 + storageClassName: your-storage-class 107 + accessModes: 108 + - ReadWriteOnce 109 + resources: 110 + requests: 111 + storage: 50Gi 112 + ``` 113 + 114 + Full Deployment example: 115 + 116 + ```yaml 117 + apiVersion: apps/v1 118 + kind: Deployment 119 + metadata: 120 + name: pds 121 + namespace: pds 122 + spec: 123 + replicas: 1 124 + selector: 125 + matchLabels: 126 + app: pds 127 + strategy: 128 + type: Recreate 129 + template: 130 + metadata: 131 + labels: 132 + app: pds 133 + spec: 134 + containers: 135 + - name: pds 136 + image: atcr.io/tranquil.farm/tranquil-pds:latest 137 + ports: 138 + - name: http 139 + containerPort: 3000 140 + env: 141 + - name: SERVER_HOST 142 + value: "0.0.0.0" 143 + - name: SERVER_PORT 144 + value: "3000" 145 + - name: PDS_HOSTNAME 146 + value: "pds.example.com" 147 + - name: PDS_USER_HANDLE_DOMAINS 148 + value: "example.com" 149 + - name: BLOB_STORAGE_BACKEND 150 + value: filesystem 151 + - name: BLOB_STORAGE_PATH 152 + value: /var/lib/tranquil/blobs 153 + - name: CRAWLERS 154 + value: "https://bsky.network" 155 + - name: INVITE_CODE_REQUIRED 156 + value: "true" 157 + envFrom: 158 + - secretRef: 159 + name: pds-secrets 160 + volumeMounts: 161 + - mountPath: /var/lib/tranquil/blobs 162 + name: blobs 163 + readinessProbe: 164 + httpGet: 165 + path: /xrpc/_health 166 + port: http 167 + initialDelaySeconds: 15 168 + periodSeconds: 10 169 + failureThreshold: 6 170 + resources: 171 + requests: 172 + memory: 256Mi 173 + cpu: 100m 174 + limits: 175 + memory: 1Gi 176 + volumes: 177 + - name: blobs 178 + persistentVolumeClaim: 179 + claimName: pds-blobs 180 + ``` 181 + 182 + `SERVER_HOST: "0.0.0.0"` is required. The default `127.0.0.1` won't be reachable by the Kubelet or your ingress controller. 183 + 184 + --- 185 + 186 + ## Ingress 187 + 188 + The ingress needs rules for both the PDS hostname and the wildcard for user handles. 189 + 190 + ```yaml 191 + apiVersion: networking.k8s.io/v1 192 + kind: Ingress 193 + metadata: 194 + name: pds 195 + namespace: pds 196 + spec: 197 + tls: 198 + - hosts: 199 + - pds.example.com 200 + - "*.example.com" 201 + secretName: pds-tls 202 + rules: 203 + - host: pds.example.com 204 + http: 205 + paths: 206 + - path: / 207 + pathType: Prefix 208 + backend: 209 + service: 210 + name: pds 211 + port: 212 + number: 3000 213 + - host: "*.example.com" 214 + http: 215 + paths: 216 + - path: / 217 + pathType: Prefix 218 + backend: 219 + service: 220 + name: pds 221 + port: 222 + number: 3000 223 + ``` 224 + 225 + --- 23 226 24 227 ## Custom homepage 25 228 ··· 27 230 28 231 ```yaml 29 232 apiVersion: v1 233 + kind: ConfigMap
+27 -4
example.toml
··· 1 + # Configuration is loaded in this order: 2 + # 3 + # 1. Environment variables (highest priority, always win) 4 + # 2. A custom config file, either passed with --config or the file 5 + # referenced in the TRANQUIL_PDS_CONFIG environment variable 6 + # 3. /etc/tranquil-pds/config.toml (always loaded regardless of config values) 7 + # 8 + # Useful commands: 9 + # tranquil-pds validate - validate your config without starting 10 + # tranquil-pds config-template - generate a commented example.toml 11 + 1 12 [server] 2 13 # Public hostname of the PDS, such as `pds.example.com`. 3 14 # ··· 142 153 #acquire_timeout_secs = 10 143 154 144 155 [secrets] 145 - # Secret used for signing JWTs. Must be at least 32 characters in 156 + # Secret used for signing session JWTs. Must be at least 32 characters in 146 157 # production. 147 158 # 148 159 # Can also be specified via environment variable `JWT_SECRET`. ··· 355 366 # Default value: 4 356 367 #max_concurrent_repo_exports = 4 357 368 358 - # List of relay / crawler notification URLs. 369 + # List of relay / crawler notification URLs. Notified when new events are 370 + # committed to an account's repo. 371 + # 372 + # Defaults to [ "https://bsky.network" ] when unset. 359 373 # 360 374 # Can also be specified via environment variable `CRAWLERS`. 361 375 #crawlers = 362 376 363 377 [email] 364 - # Sender email address. When unset, email sending is disabled. 378 + # Sender email address. When unset, email sending is disabled entirely. 379 + # 380 + # Email is optional, but at least one comms method (email, Discord, 381 + # Telegram, or Signal) must be set up for account verification, 382 + # password resets, and 2FA backup codes to work. 365 383 # 366 384 # Can also be specified via environment variable `MAIL_FROM_ADDRESS`. 367 385 #from_address = ··· 469 487 #require_tls = false 470 488 471 489 [email.dkim] 472 - # DKIM selector. When unset, outgoing mail is not signed. 490 + # DKIM signing configuration. 491 + # 492 + # The corresponding DNS TXT record at <selector>._domainkey.<domain> must 493 + # be published before mail is sent. 494 + # 495 + # DKIM selector. When unset, outgoing mail is not DKIM-signed. 473 496 # 474 497 # Can also be specified via environment variable `MAIL_DKIM_SELECTOR`. 475 498 #selector =

History

4 rounds 0 comments
sign up or login to add to the discussion
6 commits
expand
adding k8s docs and a more generalized doc
oops didnt actually mean to take that out
removing gen config altogether, agree with nel
expanding example.toml a tiny bit and reworking k8s a little
rewording, and some sentence simplification
more rewording
merge conflicts detected
expand
expand 0 comments
5 commits
expand
adding k8s docs and a more generalized doc
oops didnt actually mean to take that out
removing gen config altogether, agree with nel
expanding example.toml a tiny bit and reworking k8s a little
rewording, and some sentence simplification
expand 0 comments
2 commits
expand
adding k8s docs and a more generalized doc
oops didnt actually mean to take that out
expand 0 comments
dsx.sh submitted #0
1 commit
expand
adding k8s docs and a more generalized doc
expand 0 comments