Files
Scooby Husky a095c27500 Fix CNPG distributed-topology validation errors
Confirmed live against the vcluster.cnpg.io admission webhook: (1)
replica.enabled is mutually exclusive with primary/self - they're two
different mechanisms (old single-source replica-cluster mode vs the
newer distributed-topology model), not composable. (2) Both replica.self
and replica.primary must reference names present in THAT cluster's own
externalClusters list - including a self-referencing entry, even though
it describes the cluster itself. Renamed VPS's 'home-backup' external
cluster to 'home' throughout (bootstrap.recovery.source too) so one name
consistently satisfies barmanObjectStore, connectionParameters, and the
primary reference; added matching self-referencing entries on both
sides.
2026-08-19 00:12:50 -05:00

172 lines
6.6 KiB
YAML

---
# 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=<vps minio root user> \
# --from-literal=secretkey=<vps minio root password>
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