Skip to content

Version matrix

Reproducible-from-zero infrastructure means pinning versions. "Latest" drifts; a guide that says "use the latest" is unreproducible by tomorrow. Below is the matrix this guide targets (verified June 2026). Treat the exact patch numbers as what to pin to today — always check the linked release pages and record what you actually deploy.

The components

Component Version Why / dependency
terraform-hcloud-kube-hetzner pin (e.g. 2.20.0) The platform module. Pin version, don't float.
k3s / Kubernetes what the module installs (~1.35) Needs to be a CNCF-supported release; 1.35 covers everything here.
CloudNativePG operator 1.29.1 Current stable. 1.29.1 fixes a critical CVE in the metrics exporter — do not use older.
PostgreSQL (operand) 18.3 (system-trixie) The default operand in CNPG 1.29; managed via an ImageCatalog.
kubectl-cnpg plugin match the operator (1.29.x) Install via brew install kubectl-cnpg.
Barman Cloud Plugin 0.12.0 Backup/restore via CNPG-I. Requires CNPG ≥ 1.26 and cert-manager.
cert-manager latest 1.x (verify) Required by the Barman Cloud Plugin's TLS channel. Installed by the module.
Longhorn what the module installs (pin it) Storage. Provides snapshots and S3 backups.

Security: do not skip the operator version

CloudNativePG 1.29.1 / 1.28.3 ship the fix for CVE-2026-44477 (critical, CVSS 9.4) in the metrics exporter. Since we enable monitoring metrics, an older operator would expose exactly the vulnerable component. Pin 1.29.1 or newer.

The dependency chain

Order matters. Each arrow is a "must exist before":

flowchart TB
    k8s["k3s / Kubernetes ≥ supported"] --> cm["cert-manager"]
    k8s --> lh["Longhorn + longhorn-postgres SC"]
    cm --> bcp["Barman Cloud Plugin 0.12.0"]
    k8s --> op["CNPG operator 1.29.1"]
    op --> bcp
    op --> icat["ClusterImageCatalog → PG 18.3"]
    bcp --> os["ObjectStore (R2)"]
    icat --> cl["Cluster"]
    os --> cl
    lh --> cl
    cl --> pool["Pooler"]
    cl --> sb["ScheduledBackup"]

Read it as: cert-manager and Longhorn (platform) come first; the operator and cert-manager both gate the Barman plugin; the operator gates the image catalog; the plugin gates the ObjectStore; and the Cluster needs the image catalog, the ObjectStore, and storage all in place.

Why these specific choices

  • Barman Cloud Plugin, not in-tree barmanObjectStore. The in-tree object store support is deprecated since CNPG 1.26 and scheduled for removal in 1.30. Writing a 2026 guide on the deprecated path would create immediate technical debt. The plugin is the living, supported path. The cost is two extra dependencies (cert-manager + the plugin) and one extra resource (ObjectStore).
  • ImageCatalog, not a hard-coded imageName. Image catalogs (a headline feature of CNPG 1.29) map a Postgres major version to a specific, pinned, signed operand image. It centralizes image management and is the modern, fleet-friendly approach.
  • PostgreSQL 18. Released September 2025, supported into 2030; it is the current default operand and unlocks features like image-volume extensions.

How to capture versions for reproducibility

Because Layer 1 add-on versions come from the module, record what was actually deployed so you can pin it:

# Operator image (and thus version)
kubectl get deployment -n cnpg-system cnpg-controller-manager \
  -o jsonpath='{.spec.template.spec.containers[*].image}'

# cert-manager and Longhorn versions
kubectl get pods -n cert-manager -o jsonpath='{.items[*].spec.containers[*].image}'
kubectl get pods -n longhorn-system -o jsonpath='{.items[*].spec.containers[*].image}'

# Kubernetes / k3s
kubectl version

Put the results into your kube.tf (cert_manager_version, longhorn_version) and into your manifests, then never rely on "latest" again.

Where to go deeper

Next: the build begins with Layer 1 — the platform.