CloudNativePG and Garage¶
The CloudNativePG operator runs Postgres as Cluster resources, and Garage is the
in-cluster S3 store their base backups and WAL archive go to.
Operator¶
k8s/talos/infra/cloudnative-pg/ renders two charts into cnpg-system:
cloudnative-pg: the operator and its CRDs (postgresql.cnpg.io). Its own metrics are scraped through the chart's PodMonitor.plugin-barman-cloud: the Barman Cloud plugin and itsObjectStoreCRD (barmancloud.cnpg.io). It must run in the operator's namespace. It gets its TLS certificates from a self-signed cert-managerIssuerthe chart creates, and it adds a sidecar to every instance pod of aClusterthat names it underplugins; that sidecar is what writes to S3.
Both pin their image tag to the chart's appVersion, so a chart bump is the image bump.
CRD changes ride along through includeCRDs.
Garage¶
k8s/talos/infra/garage/ runs a single-node Garage (replication_factor = 1) in the
garage namespace. The S3 endpoint is http://garage.garage.svc:3900, region garage,
path-style addressing.
| Volume | Class | Holds |
|---|---|---|
garage-data |
syno-nfs-csi |
object blocks under data/, metadata snapshots under meta-snapshots/ |
garage-meta |
proxmox-local |
the live LMDB metadata |
LMDB corrupts on NFS, so the metadata lives on block storage and Garage snapshots it to
the NAS volume every hour. garage-data sits on the k8s-volumes share, which has no
snapshots or offsite copy, so VolSync backs it up nightly at 01:50 with restic into
k8s-backups/volsync/garage/garage-data, which the NAS snapshots and Hyper Backup cover
(../backups/README.md). garage-meta is not backed up: a live
LMDB copy can be torn, and the hourly snapshots in garage-data replace it.
The pod runs as uid 1000 with fsGroup: 1000. The NFS driver's File fsGroupPolicy
lets kubelet hand the fresh, root-only NAS directory to that group on first mount
(../storage/README.md).
The network policy admits S3 only from CloudNativePG pods (any pod labelled
cnpg.io/cluster, which covers recovery jobs) in the namespaces listed there. A new database namespace
has to be added to it.
Secrets¶
| Bitwarden secret | Used by |
|---|---|
garage-rpc-secret |
Garage RPC, garage-secrets in garage |
garage-admin-token |
admin API, garage-secrets in garage |
garage-logeverylift-access-key-id, garage-logeverylift-secret-access-key |
the S3 key for logeverylift's bucket |
Bootstrap¶
The cluster layout, buckets and keys are Garage state, not manifests. On a new or rebuilt Garage, run once from the laptop:
export KUBECONFIG=~/Documents/Code/github/Homelab/terraform/proxmox/hyper-cluster/k8s/talos/kubeconfig
garage() { kubectl exec -n garage deploy/garage -- /garage "$@"; }
NODE=$(garage node id -q | cut -d@ -f1)
garage layout assign -z homelab -c 20G "$NODE"
garage layout apply --version 1
garage status
Then per database, a bucket and a key imported from Bitwarden, so the ID and secret never pass through the terminal:
bws_get() { BWS_ACCESS_TOKEN=$(bws-token) bws secret get "$1" --output json | jq -r .value; }
garage bucket create cnpg-logeverylift
garage key import --yes -n logeverylift \
"$(bws_get 5e61c8e6-3642-4bbc-8a6a-b4f400f0eb33)" \
"$(bws_get 48d4180c-091c-48ec-8971-b4f400f0ecb3)" > /dev/null
garage bucket allow --read --write --owner cnpg-logeverylift --key logeverylift
garage bucket info cnpg-logeverylift shows its size and object count.
Emptying a server's prefix¶
A new Cluster refuses to archive into a prefix that already holds WAL, so a Cluster
recreated under the same serverName (after a failed cutover, say) needs its old
prefix removed first. Garage has no CLI for objects; use any S3 client through a
port-forward, which the network policy admits as host traffic:
kubectl port-forward -n garage svc/garage 13900:3900 &
bws_get() { BWS_ACCESS_TOKEN=$(bws-token) bws secret get "$1" --output json | jq -r .value; }
docker run --rm \
-e AWS_ACCESS_KEY_ID="$(bws_get 5e61c8e6-3642-4bbc-8a6a-b4f400f0eb33)" \
-e AWS_SECRET_ACCESS_KEY="$(bws_get 48d4180c-091c-48ec-8971-b4f400f0ecb3)" \
-e AWS_DEFAULT_REGION=garage amazon/aws-cli:2.27.50 \
--endpoint-url http://host.docker.internal:13900 \
s3 rm --recursive s3://cnpg-logeverylift/<serverName>/
Restoring Garage itself¶
If garage-meta is lost, point metadata_dir at a copy of the newest directory under
meta-snapshots/ (or restore the PVC and copy it in), start Garage, and the objects on
the NAS are readable again. If garage-data is lost, restore it from VolSync first
(../backups/restore.md), then the metadata from the
snapshot inside it. Objects written after that snapshot are not in its index, so a
restore from the nightly copy can lose up to an hour of WAL; the nightly pg_dump is
the fallback.