apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: shared-postgres namespace: cnpg spec: imageName: ghcr.io/cloudnative-pg/postgresql:17.2-4 instances: 3 enableSuperuserAccess: true # ── Topology-aware placement ── # Hetzner CCM labels nodes automatically: # topology.kubernetes.io/zone = fsn1 | nbg1 | hel1 (datacenter) # topology.kubernetes.io/region = eu-central | hel-southeast # # Layer 1: podAntiAffinity on hostname (hard constraint) # → No 2 PG pods on the same node. Ever. # # Layer 2: podAntiAffinity on zone (soft constraint) # → Prefer spreading across datacenters. With nodes in fsn1, nbg1, hel1, # you get 1 pod per DC. With only eu-central nodes, they still spread # across FSN and NBG (both in eu-central, ~1-2ms apart). # # Prerequisite for cross-region (eu-central + HEL): # - Node pool in eu-central (FSN + NBG) → aula-08 # - Node pool in hel-southeast (HEL) → add worker after aula-08 # - Without HEL nodes, all 3 pods stay in eu-central (still HA within region) affinity: topologyKey: kubernetes.io/hostname podAntiAffinityType: required podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: topologyKey: topology.kubernetes.io/zone labelSelector: matchLabels: cnpg.io/cluster: shared-postgres storage: size: 10Gi storageClass: hcloud-volumes walStorage: size: 5Gi storageClass: hcloud-volumes resources: requests: cpu: 500m memory: 1Gi limits: cpu: "1" memory: 1Gi postgresql: parameters: max_connections: "100" shared_buffers: "256MB" effective_cache_size: "512MB" work_mem: "4MB" maintenance_work_mem: "128MB" log_min_duration_statement: "1000" bootstrap: initdb: database: app owner: app secret: name: shared-postgres-superuser superuserSecret: name: shared-postgres-superuser backup: barmanObjectStore: destinationPath: s3://CHANGE_BUCKET_NAME/cnpg-backups/shared-postgres endpointURL: https://CHANGE_ENDPOINT s3Credentials: accessKeyId: name: cnpg-backup-credentials key: ACCESS_KEY_ID secretAccessKey: name: cnpg-backup-credentials key: ACCESS_SECRET_KEY wal: compression: gzip data: compression: gzip jobs: 2 retentionPolicy: "30d" monitoring: enablePodMonitor: true startDelay: 3600 livenessProbeTimeout: 90 primaryUpdateStrategy: unsupervised primaryUpdateMethod: switchover