cnpg-drill
Prove that a CloudNativePG backup can restore. cnpg-drill creates a disposable CloudNativePG cluster from the source cluster's Barman Cloud object store, waits for PostgreSQL to become ready, runs read-only SQL assertions, emits a JSON result, and deletes the drill cluster. It can run locally or on a schedule as a Kubernetes CronJob.
The core is free and works without an account or external service. It does not make backups or copy database contents to a vendor. The proposed hosted service in RESEARCH.md is separate and has not been built.
Status
Early alpha. Unit tests cover manifest safety and execution state. A disposable local integration run on v0.1.0 proved full restore, PITR, read-only archive access, denied-WAL reporting, failed-check reporting, and Cluster/PVC cleanup on one version matrix. The v0.1.1 release archive and public chart 0.1.2 also restored a fresh backup and checked a row in an application database. A separate single-node pgEdge chart restore passed SQL checks in its app database. Validate against your operator, PostgreSQL image, storage, object store, and Barman plugin versions before treating a pass as disaster recovery assurance.
The v0.1.2 release carries literal AWS checksum compatibility settings into recovery clusters without copying source credentials. It includes the MIT-licensed kubectl plugin archive and checksum. The default Krew index submission and custom Krew index submission currently package v0.1.0 and are under review. kubetools is reviewing a Backup Tools listing.
The Helm chart is listed on Artifact Hub as a Verified Publisher package. Chart version 0.1.2 uses the v0.1.1 application image and includes the publisher's website.
What it supports
- One source CloudNativePG cluster with one enabled Barman Cloud plugin and a named
ObjectStorein the same namespace. - An optional separate
recoveryObjectStorewith credentials limited to archive reads. Preflight checks that its destination and endpoint match the source store before creating a Cluster. - Full restore from the latest completed Barman plugin
Backupresource, or PITR to an explicit RFC3339targetTime. The selected backup ID is pinned and included in the report; a stale backup fails preflight. - Single-instance drill cluster using the source's PostgreSQL image, storage size/class, optional WAL storage, and resource requests.
- Copies literal AWS checksum compatibility settings from the source Cluster for S3-compatible archive restores; it does not copy writer credentials or arbitrary environment variables.
- Read-only SQL checks in
postgresor a named application database. Exact expected values make reports easy to audit. - Optional timestamp freshness assertions (
maxAgeSeconds) for an application-defined recovery point. This measures age of that application's data, not the database WAL RPO. - A JSON report on stdout and optionally in a local file. Query outputs are hashed rather than logged. Exit code 0 means all checks, Cluster deletion, and PVC cleanup passed; 1 means the drill failed; 2 means invalid input or a failed preflight.
- Scheduled execution with the Helm CronJob in
deploy/helm/cnpg-drill.
The CLI refuses source clusters with tablespaces or recovery bootstrap because a naive clone could produce an incomplete or misleading pass. Volume snapshots, cross-namespace restores, cross-region recovery, custom Postgres extension image changes, and multi-cluster fleet management are future work.
Install and use
For a first run on your own cluster, follow the first-run guide.
Requirements: Python 3.10+, kubectl in PATH, CloudNativePG and the Barman Cloud plugin installed in the target cluster, and Kubernetes access to read the source Cluster and ObjectStore, create/get/delete a drill Cluster, and exec into its PostgreSQL pod.
python3 -m pip install -e .
cnpg-drill plan --config examples/drill.json
cnpg-drill run --config examples/drill.json --report reports/latest.json
plan reads the source configuration and prints the exact Cluster manifest it would create. Review that manifest before running the first drill. The example's table-count assertion is illustrative; replace it with an invariant from your own application. A minimal check is SELECT 1, but it only proves connection to the recovered server, not useful application data.
Example drill.json:
{
"namespace": "production",
"cluster": "app-db",
"recoveryObjectStore": "app-db-recovery-readonly",
"timeoutSeconds": 1800,
"maxBackupAgeSeconds": 691200,
"checks": [
{"name": "postgres-ready", "query": "SELECT 1", "expected": "1"},
{"name": "orders-exist", "database": "app", "query": "SELECT count(*) > 0 FROM public.orders", "expected": "t"},
{"name": "recent-order", "database": "app", "query": "SELECT max(created_at)::timestamptz FROM public.orders", "maxAgeSeconds": 86400}
]
}
For PITR, add "targetTime": "2026-09-29T10:00:00Z". Pick a time inside the backup and WAL retention window. A passing PITR check proves only that target; use a second drill for the latest recovery path.
Scheduled drill
The Helm chart creates a namespace-scoped ServiceAccount, Role, RoleBinding, ConfigMap, and CronJob. Set cluster, checks, and the other settings in a values file and install it in the same namespace as the source Cluster and ObjectStore. The chart defaults to suspension so installation does not immediately start a restore.
The example image build pins kubectl 1.36.4, suitable for Kubernetes 1.35–1.37 under the project's version skew policy. Set KUBECTL_VERSION at image build time for another supported cluster version.
helm install cnpg-drill oci://ghcr.io/danielgaskins/charts/cnpg-drill \
--version 0.1.2 -n production -f drill-values.yaml
See the first-run guide and chart values for a suspended first run with a separate read-only recovery ObjectStore. The chart pins a public multi-architecture image digest. The CronJob uses concurrencyPolicy: Forbid and a bounded job deadline. Its logs contain the JSON result; failed runs have nonzero exit status. The chart does not yet provide durable report storage, missed-run alerts, or fleet policy.
Safety boundary
- The generated recovery Cluster never copies
spec.plugins, so it is not configured to archive WAL back to the source bucket. SetrecoveryObjectStoreto a separately namedObjectStorewith read-only archive credentials. The tool compares its destination and endpoint with the source store; it cannot verify credential permissions, so test that writes are denied before relying on this boundary. Omitting this field uses the sourceObjectStorefor compatibility. - A recovery timeout includes a machine-readable
failureReasonwhen the recovery Pod logs identify archive access denial or unavailable WAL. It does not copy raw logs into the report; inspect Pod and operator logs for detail. - The drill uses the same namespace because the plugin's
ObjectStoreand its credentials are namespace-scoped. The source Cluster object is never modified. - SQL checks run in a
BEGIN READ ONLYtransaction. Queries are limited to oneSELECTorWITHstatement without semicolons. Only provide trusted SQL; this is a guard against mistakes, not a security sandbox. - By default, the tool deletes the drill Cluster even when recovery or a check fails.
retainOnFailureleaves it for investigation and can incur storage costs. Cleanup failures turn the result red. - The tool checks that PVCs labeled for the drill cluster disappear after Cluster deletion and fails the report if they remain. Kubernetes PersistentVolume cleanup depends on the storage class; inspect cloud volumes after a first drill.
- The planned SaaS boundary is metadata only. This repository contains no telemetry upload or hosted service.
Contributing and upstream distribution
Run PYTHONPATH=src python3 -m unittest discover -s tests -v. See CONTRIBUTING.md for how to add a recovery path, the live integration test gate, and the Krew package plan. We will submit only a tested, released package that stands on its own. Upstream acceptance is not assumed.
UPSTREAM.md records the specific repositories, proposed contribution value, and submission gates.
Recovery references
Metadata
Release files for cnpg-drill 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| cnpg_drill-0.1.2.tar.gz | 21.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cnpg_drill-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.7 kB
Release files / cnpg_drill-0.1.2.tar.gz
| Download URL | cnpg_drill-0.1.2.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6343a907b9a5dea013e2d059ad2250c8e885de9f777fe9d10decb6b8ade9cfd1
|
|
BLAKE2b-256 checksum How to use checksums |
b92e24868adf086ff1eb07f7749889865da2ff27e6e1b429a2f426d505f1ca08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / cnpg_drill-0.1.2-py3-none-any.whl
| Download URL | cnpg_drill-0.1.2-py3-none-any.whl |
|---|---|
| Size | 14.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4e960b37a63cae7f1b4c0efb94d94a057a3c4b540399601551c8bd529db1f447
|
|
BLAKE2b-256 checksum How to use checksums |
72ec1b7869779a55cf6bd9ba240ad21fa5db3872f31fb52da79d39fcc06f17f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logSecurity for cnpg-drill 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for cnpg_drill-0.1.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.