--- # CNPG "replica cluster" - continuously replays WAL shipped from home's # pg-authentik cluster (infrastructure/authentik/manifests/cnpg-cluster.yaml) # via VPS MinIO's cnpg-backups/pg-authentik bucket path. Unlike Vault's # raft-restore CronJob (Vault has no native continuous-replication-into- # object-store feature), CNPG's replica-cluster mode is a first-class # built-in mechanism - no custom restore scripting needed, matches the # plan's intent directly. # # While spec.replica.enabled is true, this cluster is a read-only standby # in continuous recovery - the -rw service exists but is NOT writable # (same "nothing accepts writes in normal operation" discipline as Vault/ # Gitea). Promoting it to a real writable primary during an actual # incident is a deliberate manual step: # kubectl -n authentik patch cluster pg-authentik --type merge \ # -p '{"spec":{"replica":{"enabled":false}}}' # then scale the authentik Deployment(s) up from 0. # # vps-minio-secret is a plain Secret copied here manually (kubectl, not # git) from the vps-minio-root-secret in the minio namespace - this # cluster has no Vault/ESO of its own: # kubectl -n authentik create secret generic vps-minio-secret \ # --from-literal=accesskey= \ # --from-literal=secretkey= apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: pg-authentik namespace: authentik spec: imageName: ghcr.io/cloudnative-pg/postgresql:16 instances: 1 # shared_buffers: 512MB above needs headroom - matches home's requests/limits. resources: requests: memory: "512Mi" cpu: "25m" limits: memory: "2Gi" cpu: "250m" storage: size: 5Gi storageClass: local-path # Must match (or exceed) home's max_connections/max_wal_senders/etc - # Postgres refuses to replay WAL past a checkpoint recorded with higher # values than the recovering instance's own settings ("recovery aborted # because of insufficient parameter settings: max_connections = 100 is # a lower setting than on the primary server, where its value was 200", # confirmed live 2026-08-18). Copied from # infrastructure/authentik/manifests/cnpg-cluster.yaml. postgresql: parameters: max_connections: "200" shared_buffers: "512MB" effective_cache_size: "1536MB" maintenance_work_mem: "128MB" checkpoint_completion_target: "0.9" wal_buffers: "16MB" default_statistics_target: "100" random_page_cost: "1.1" effective_io_concurrency: "200" work_mem: "2621kB" min_wal_size: "1GB" max_wal_size: "4GB" bootstrap: recovery: source: home # Multi-site active failover pilot (see # /home/scooby/.claude/plans/jiggly-snacking-iverson.md) - the # bootstrap.recovery above (barmanObjectStore) stays exactly as-is for # the initial seed; connectionParameters below adds real streaming # replication for ongoing sync, replacing the old WAL-archive-polling # behavior (the source of the crash-looping/probe-tuning fights from # earlier tonight - streaming doesn't need to restart postgres to check # for new WAL). # # Auth: reuses CNPG's own generated streaming_replica client cert # (CN=streaming_replica, issued by home's own pg-authentik CA) - copied # here manually as pg-authentik-home-{ca,replication} (kubectl, not # git). Same credential CNPG's own pg_hba.conf already trusts, nothing # new to provision. # # Reachable via home.kube.huskypup.net:61432 - a UniFi WAN port-forward # to talos-cp-01's real LAN IP (172.28.101.41), source-IP-restricted to # just the VPS and witness public IPs. NOT the Netbird mesh directly - # UniFi has no route to that CIDR, confirmed live (see # infrastructure/ha-failover/manifests/etcd.yaml for the full story). # externalClusters[].name is now "home" (was "home-backup") - reused # consistently for bootstrap.recovery.source, replica.primary, AND the # connectionParameters below, since CNPG's distributed-topology # validation requires replica.primary to match an externalClusters name # exactly (confirmed live: "External cluster home not found" when it # didn't). serverName inside barmanObjectStore is unaffected by this # rename - it's an explicit separate override, not derived from the # entry name. # # Also needs a self-referencing "vps" entry below (same reason - # replica.self must match an externalClusters name too, even though # it's this very cluster) - its connectionParameters point at VPS's own # local -rw service, never actually dialed while this cluster is a # replica. externalClusters: - name: home barmanObjectStore: # serverName defaults to the externalClusters[].name here, NOT # the source Postgres cluster's actual name - but home's # barmanObjectStore backup: block (on the pg-authentik Cluster # itself) defaults serverName to its own metadata.name # ("pg-authentik"). Without this override, recovery looks for # backups under the wrong server-name prefix and finds none - # "no target backup found" despite the backup genuinely existing # (confirmed live 2026-08-18). serverName: pg-authentik destinationPath: s3://cnpg-backups/pg-authentik endpointURL: http://vps-minio.minio.svc.cluster.local:9000 s3Credentials: accessKeyId: name: vps-minio-secret key: accesskey secretAccessKey: name: vps-minio-secret key: secretkey connectionParameters: host: home.kube.huskypup.net port: "61432" dbname: app user: streaming_replica sslmode: verify-ca sslCert: name: pg-authentik-home-replication key: tls.crt sslKey: name: pg-authentik-home-replication key: tls.key sslRootCert: name: pg-authentik-home-ca key: ca.crt - name: vps connectionParameters: host: pg-authentik-rw port: "5432" dbname: app user: streaming_replica sslmode: verify-ca sslCert: name: pg-authentik-replication key: tls.crt sslKey: name: pg-authentik-replication key: tls.key sslRootCert: name: pg-authentik-ca key: ca.crt # Distributed topology: both sides agree home is primary today. No # replica.enabled - that flag belongs to CNPG's older single-source # replica-cluster mode and is mutually exclusive with primary/self # (confirmed live: "replica mode enabled is not compatible with the # primary field"). replica: self: vps primary: home source: home monitoring: enablePodMonitor: false # no Prometheus on the VPS cluster