arrstack: run sonarr/radarr/prowlarr active-active on shared Postgres #383
Reference in New Issue
Block a user
Delete Branch "benvin/arrstack-active-active"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
The arrstack
sonarr/radarr/prowlarrapps currently run as single-replica upstream LinuxServer images on SQLite (RWO config PVC,Recreatestrategy) — no HA, no horizontal scale, and a hard one-pod ceiling because SQLite can't be shared. Our-unkin2forks move all app state into PostgreSQL and use Postgres advisory locks for cross-replica coordination (migrations, scheduled tasks, command execution), which makes an active-active multi-replica deployment safe.This is a fresh Postgres backend — no SQLite data migration. The current single-replica pods are throwaway baselines; the new pods start on an empty per-app database and the fork's provider migrates the schema on first start.
arrproxy(its ownarrproxy-db) andnzbgetare untouched.What
New
apps/base/arrstack/postgres/:cnpg_cluster.yaml— CNPGClusterarrstack-postgres(3 instances,cephrbd-fast-delete), throwawayappinitdb owner, and 3managed.roles(sonarr/radarr/prowlarr, login non-superuser) each withpasswordSecret: <app>-db.database-{sonarr,radarr,prowlarr}.yaml— CNPGDatabase<app>-mainowned by<app>,databaseReclaimPolicy: retain.cnpg_backup.yaml— cephrgwObjectStoreUser+Bucket cnpg-arrstack+ nightlyScheduledBackup(mirrors jellyfin).vaultstaticsecret.yaml— 3 VSOVaultStaticSecrets syncing<app>-db(keysusername/password) fromkv/kubernetes/namespace/arrstack/default/<app>-db(already seeded); these are both the CNPG rolepasswordSecretand the Deployment's Postgres creds.<app>-dbSecrets (0) → Cluster (1) → Databases (2).postgrestoapps/base/arrstack/kustomization.yaml.Per app (
sonarr/radarr/prowlarr):deployment.yaml: image →docker-internal/<app>:v<base>-unkin2(sonarrv5.0.0, radarrv6.4.2, prowlarrv2.6.2);replicas: 3;RollingUpdate. Removed theapikey-initinitContainer andPUID/PGID/TZ. Runs the binary directly (command: [/app/<Bin>],args: [-nobrowser, -data=/config, -nosingleinstancecheck]). Env from the new ConfigMap +secretKeyRef:<App>__Postgres__User/__Password(Secret<app>-db) and<App>__Auth__ApiKey(existing<app>-apikey, keyapitoken)./<app>/pingprobes kept.securityContextrunAsUser/runAsGroup/fsGroup 1000 for CephFS group-write.configmap.yaml: non-secret<App>__…env (Postgres Host/Port/MainDb,Log__DbEnabled=false,Auth__Method=External,Auth__Required=DisabledForLocalAddresses,App__InstanceName,Server__Port,Server__UrlBase=/<app>,Update__Mechanism=External).pvc-config.yaml→ RWXcephfs-raid5-retain(shared/configforconfig.xml/MediaCover).service.yaml,gateway.yaml,httproute.yaml, apikeyvaultstaticsecret.yaml— arrproxy still addresses the Service VIP, so N replicas are transparent.Validation
kustomize build apps/overlays/au-syd1/arrstackclean;kubeconform64/64 valid;yamllintand the repo pre-commit (incl. no-plain-secrets) pass. Runtime validation (pods Ready, single migrate under advisory lock, scheduled-task/command single-owner, UI viahttps://arrstack.unkin.net/<app>/, 3→1→3 rescale) is post-merge once ArgoCD syncs.Decisions / unknowns
-unkin2images have noUSER(run as root); pinnedrunAsUser/runAsGroup/fsGroup: 1000withfsGroupChangePolicy: OnRootMismatch. Verify group-write on the shared CephFS/configon first deploy.cephfs-raid5-retainshared/config(per spec preference) over per-podemptyDir.cephrbd-fast-delete10Gi, PG17, 3 instances.Correctness review — verdict: essentially CLEAN. Verified the env contract against the fork source at
v5.0.0-unkin2(unkin/sonarr): all keys bind to real Options classes (PostgresOptions/AuthOptions/ServerOptions/AppOptions/UpdateOptions/LogOptions), andSonarr__Log__DbEnabled=falsecorrectly routes toAddDummyLogDatabase()(Bootstrap.cs:100/241) so the absentPostgres__LogDb(only<app>-mainprovisioned) is fine.kustomize build+kubeconformclean (64 resources, 0 invalid). Config PVCs are RWXcephfs-raid5-retain(multi-attach safe), Services/ports/UrlBase unchanged, arrproxy reads the sameapitokenkey the apps now consume — no routing break. CNPG managed-role→VSS-Opaque-secret pattern mirrors the provenapps/base/puppetcluster. Sync-waves (secret 0 → cluster 1 → db 2) are ordered right.Two pre-deploy items (not manifest bugs, but boot-blocking if wrong):
Seeded
usernamemust equal the role name. Each app connects as<app>-db.username(ConnectionStringFactory→Npgsql ... Username) and its DB<app>-mainis owned by role<app>. On a fresh DB the connecting role must BE the owner to create the schema, and CNPG also uses this same secret as the rolepasswordSecret. Sokv/kubernetes/namespace/arrstack/default/<app>-dbmust haveusername=sonarr|radarr|prowlarrrespectively (not a shared/other name) or first-boot migration fails with a permissions error. Worth a quick confirm before sync.Nit (optional): add a
startupProbe. liveness isinitialDelay 30 / period 30 / failureThreshold 3(~120s budget). First boot has one replica migrating under the advisory lock while the other two wait; empty-schema migration is fast so this is low-risk, but astartupProbewould harden the RollingUpdate against lock-wait if migrations ever grow.No blocking issues; safe to merge once (1) is confirmed.