{"id":51326445,"url":"https://github.com/odit-services/cnpg-plugin-pgdump","last_synced_at":"2026-07-01T19:00:16.904Z","repository":{"id":367580835,"uuid":"1281377284","full_name":"odit-services/cnpg-plugin-pgdump","owner":"odit-services","description":"A CloudNativePG(-I) plugin that allow creation of logical backups through pg_dump.","archived":false,"fork":false,"pushed_at":"2026-06-26T15:18:05.000Z","size":72,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-26T16:26:39.432Z","etag":null,"topics":["backup","cloudnativepg","cnpg-i","pgdump","plugin","postgresql"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/odit-services.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-26T13:50:22.000Z","updated_at":"2026-06-26T15:18:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/odit-services/cnpg-plugin-pgdump","commit_stats":null,"previous_names":["odit-services/cnpg-plugin-pgdump"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/odit-services/cnpg-plugin-pgdump","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odit-services%2Fcnpg-plugin-pgdump","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odit-services%2Fcnpg-plugin-pgdump/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odit-services%2Fcnpg-plugin-pgdump/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odit-services%2Fcnpg-plugin-pgdump/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/odit-services","download_url":"https://codeload.github.com/odit-services/cnpg-plugin-pgdump/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odit-services%2Fcnpg-plugin-pgdump/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35019037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backup","cloudnativepg","cnpg-i","pgdump","plugin","postgresql"],"created_at":"2026-07-01T19:00:14.287Z","updated_at":"2026-07-01T19:00:16.876Z","avatar_url":"https://github.com/odit-services.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CNPG pg_dump Backup Plugin\n\nCNPG-I plugin for CloudNativePG v1.26+ that performs logical PostgreSQL backups with `pg_dump -Fc` and uploads one dump per database to S3.\n\nThe plugin uses `ReconcilerHooks.Pre` for `Backup` reconciliation. On success it returns `BEHAVIOR_TERMINATE`, so the CNPG operator skips its physical backup flow. On failure it logs/stores the error and returns `BEHAVIOR_CONTINUE`.\n\n## Supported PostgreSQL Versions\n\nThe image bundles `pg_dump` from these PostgreSQL major versions:\n\n| Version | pg_dump binary |\n|---------|----------------|\n| 14      | `pg_dump-14`   |\n| 15      | `pg_dump-15`   |\n| 16      | `pg_dump-16`   |\n| 17      | `pg_dump-17`   |\n| 18      | `pg_dump-18`   |\n\nAt backup time the plugin reads `SHOW server_version_num` and selects the matching `pg_dump` binary — guaranteeing same-major restore compatibility. Unsupported versions fail with a clear error.\n\n## Quickstart\n\nPrerequisites:\n\n- CloudNativePG v1.26+ is installed.\n- `kubectl` is available locally.\n- cert-manager is installed, or `openssl` is available for the manual fallback below.\n- An S3-compatible bucket exists.\n\nConfigure the CNPG operator to discover this plugin:\n\n```sh\nkubectl apply -f - \u003c\u003c'EOF'\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: cnpg-controller-manager-config\n  namespace: cnpg-system\ndata:\n  INCLUDE_PLUGINS: pgdump-backup.cloudnative-pg.io\nEOF\nkubectl -n cnpg-system rollout restart deployment/cnpg-controller-manager\nkubectl -n cnpg-system rollout status deployment/cnpg-controller-manager\n```\n\nCreate the CNPG-I mTLS Secrets referenced by `kubernetes/deployment.yaml`.\n\nRecommended cert-manager setup:\n\n```sh\nkubectl apply -f - \u003c\u003c'EOF'\napiVersion: cert-manager.io/v1\nkind: Issuer\nmetadata:\n  name: cnpg-plugin-pgdump-selfsigned\n  namespace: cnpg-system\nspec:\n  selfSigned: {}\n---\napiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n  name: cnpg-plugin-pgdump-ca\n  namespace: cnpg-system\nspec:\n  secretName: cnpg-plugin-pgdump-ca\n  isCA: true\n  commonName: cnpg-plugin-pgdump-ca\n  duration: 8760h\n  renewBefore: 720h\n  issuerRef:\n    name: cnpg-plugin-pgdump-selfsigned\n---\napiVersion: cert-manager.io/v1\nkind: Issuer\nmetadata:\n  name: cnpg-plugin-pgdump-ca\n  namespace: cnpg-system\nspec:\n  ca:\n    secretName: cnpg-plugin-pgdump-ca\n---\napiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n  name: cnpg-plugin-pgdump-server-tls\n  namespace: cnpg-system\nspec:\n  secretName: cnpg-plugin-pgdump-server-tls\n  duration: 2160h\n  renewBefore: 360h\n  issuerRef:\n    name: cnpg-plugin-pgdump-ca\n  commonName: cnpg-plugin-pgdump.cnpg-system.svc\n  dnsNames:\n    - cnpg-plugin-pgdump\n    - cnpg-plugin-pgdump.cnpg-system\n    - cnpg-plugin-pgdump.cnpg-system.svc\n    - cnpg-plugin-pgdump.cnpg-system.svc.cluster.local\n---\napiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n  name: cnpg-plugin-pgdump-client-tls\n  namespace: cnpg-system\nspec:\n  secretName: cnpg-plugin-pgdump-client-tls\n  duration: 2160h\n  renewBefore: 360h\n  issuerRef:\n    name: cnpg-plugin-pgdump-ca\n  commonName: cnpg-plugin-pgdump-client\nEOF\n```\n\nManual OpenSSL fallback:\n\n```sh\ntmpdir=\"$(mktemp -d)\"\nopenssl req -x509 -newkey rsa:2048 -nodes \\\n  -keyout \"${tmpdir}/ca.key\" \\\n  -out \"${tmpdir}/ca.crt\" \\\n  -subj \"/CN=cnpg-plugin-pgdump-ca\" \\\n  -days 365\nopenssl req -newkey rsa:2048 -nodes \\\n  -keyout \"${tmpdir}/server.key\" \\\n  -out \"${tmpdir}/server.csr\" \\\n  -subj \"/CN=cnpg-plugin-pgdump.cnpg-system.svc\"\nprintf \"subjectAltName=DNS:cnpg-plugin-pgdump,DNS:cnpg-plugin-pgdump.cnpg-system,DNS:cnpg-plugin-pgdump.cnpg-system.svc,DNS:cnpg-plugin-pgdump.cnpg-system.svc.cluster.local\" \u003e \"${tmpdir}/server.ext\"\nopenssl x509 -req \\\n  -in \"${tmpdir}/server.csr\" \\\n  -CA \"${tmpdir}/ca.crt\" \\\n  -CAkey \"${tmpdir}/ca.key\" \\\n  -CAcreateserial \\\n  -out \"${tmpdir}/server.crt\" \\\n  -days 365 \\\n  -extfile \"${tmpdir}/server.ext\"\nopenssl req -newkey rsa:2048 -nodes \\\n  -keyout \"${tmpdir}/client.key\" \\\n  -out \"${tmpdir}/client.csr\" \\\n  -subj \"/CN=cnpg-plugin-pgdump-client\"\nopenssl x509 -req \\\n  -in \"${tmpdir}/client.csr\" \\\n  -CA \"${tmpdir}/ca.crt\" \\\n  -CAkey \"${tmpdir}/ca.key\" \\\n  -CAcreateserial \\\n  -out \"${tmpdir}/client.crt\" \\\n  -days 365\nkubectl -n cnpg-system create secret generic cnpg-plugin-pgdump-server-tls \\\n  --type=kubernetes.io/tls \\\n  --from-file=tls.crt=\"${tmpdir}/server.crt\" \\\n  --from-file=tls.key=\"${tmpdir}/server.key\" \\\n  --from-file=ca.crt=\"${tmpdir}/ca.crt\" \\\n  --dry-run=client -o yaml |\n  kubectl apply -f -\nkubectl -n cnpg-system create secret generic cnpg-plugin-pgdump-client-tls \\\n  --type=kubernetes.io/tls \\\n  --from-file=tls.crt=\"${tmpdir}/client.crt\" \\\n  --from-file=tls.key=\"${tmpdir}/client.key\" \\\n  --from-file=ca.crt=\"${tmpdir}/ca.crt\" \\\n  --dry-run=client -o yaml |\n  kubectl apply -f -\nrm -rf \"${tmpdir}\"\n```\n\nDeploy the plugin from GHCR:\n\n```sh\nkubectl apply -f https://raw.githubusercontent.com/odit-services/cnpg-plugin-pgdump/main/kubernetes/deployment.yaml\nkubectl -n cnpg-system set image deployment/cnpg-plugin-pgdump plugin=ghcr.io/odit-services/cnpg-plugin-pgdump:main\n```\n\nUse a SemVer tag instead of `main` for production, for example `ghcr.io/odit-services/cnpg-plugin-pgdump:1.2.3`. The GitHub Actions workflow publishes branch, SemVer, and SHA tags to GHCR.\n\nRelease pages also attach a rendered `cnpg-plugin-pgdump-deployment.yaml` and `cnpg-plugin-pgdump-quickstart.tar.gz`. The quickstart archive contains Kustomize, Flux, and Argo CD examples pinned to the release tag.\n\nEnable the plugin on a CNPG cluster:\n\n```yaml\napiVersion: postgresql.cnpg.io/v1\nkind: Cluster\nmetadata:\n  name: my-app-db\nspec:\n  instances: 3\n  plugins:\n    - name: pgdump-backup.cloudnative-pg.io\n      enabled: true\n  storage:\n    size: 10Gi\n```\n\nCreate S3 credentials and schedule a logical backup:\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: logical-backup-s3\ntype: Opaque\nstringData:\n  endpoint: https://minio.platform.svc:9000\n  access-key-id: minio\n  secret-access-key: minio123\n  region: us-east-1\n---\napiVersion: postgresql.cnpg.io/v1\nkind: ScheduledBackup\nmetadata:\n  name: logical-daily-backup\nspec:\n  schedule: \"0 0 2 * * *\"\n  method: plugin\n  pluginConfiguration:\n    name: pgdump-backup.cloudnative-pg.io\n    parameters:\n      target_type: s3\n      backup_user: app\n      skip_inaccessible_databases: \"false\"\n      bucket: team-backups\n      path: logical\n      retention_days: \"30\"\n      endpoint_url_secret_name: logical-backup-s3\n      region_secret_name: logical-backup-s3\n      access_key_id_secret_name: logical-backup-s3\n      secret_access_key_secret_name: logical-backup-s3\n  cluster:\n    name: my-app-db\n```\n\nCheck status:\n\n```sh\nkubectl get scheduledbackup logical-daily-backup\nkubectl get backup -l cnpg.io/scheduled-backup=logical-daily-backup\nkubectl -n cnpg-system logs deployment/cnpg-plugin-pgdump\n```\n\n## Build\n\nUsing `make`:\n\n```sh\nmake build\nmake test\nmake docker-build IMAGE=platform/cnpg-plugin-pgdump:latest\n```\n\nUsing `task`:\n\n```sh\ntask build\ntask test\ntask docker-build IMAGE=platform/cnpg-plugin-pgdump:latest\n```\n\n## E2E Tests\n\nThe Cucumber/Godog E2E suite creates a Kind cluster, installs CloudNativePG, runs RustFS as the S3 target, deploys the plugin image, and triggers a CNPG `ScheduledBackup` for each configured PostgreSQL major version.\n\nRequired local CLIs:\n\n- `docker` or `podman`\n- `kind`\n- `kubectl`\n\nThe suite uses the Kind local-registry pattern on `localhost:5001` to make the locally built plugin image available to the cluster.\n\nRun for one version:\n\n```sh\nmake e2e POSTGRES_VERSIONS=16\n```\n\nEquivalent Taskfile command:\n\n```sh\ntask e2e POSTGRES_VERSIONS=16\n```\n\nRun for multiple versions:\n\n```sh\nmake e2e POSTGRES_VERSIONS=14,15,16,17,18\n```\n\nRun multiple versions concurrently:\n\n```sh\nmake e2e POSTGRES_VERSIONS=14,15,16 E2E_PARALLELISM=3\n```\n\nEquivalent direct command:\n\n```sh\ngo test -tags=e2e ./test/e2e -count=1 -timeout=45m -postgres-versions=\"14,15,16,17,18\" -parallelism=2\n```\n\n## Runtime\n\nSee [Supported PostgreSQL Versions](#supported-postgresql-versions) for the bundled `pg_dump` binaries.\n\nConfiguration can be set with flags or environment variables:\n\n- `--listen-address`, e.g. `:50051` for TCP or `unix:///plugins` for same-pod socket setups\n- `PGDUMP_TIMEOUT`, default `12h`\n- `PGDUMP_WORKDIR`, default OS temp dir\n\nS3 configuration belongs to the `ScheduledBackup`. The plugin does not read S3 settings from Deployment environment variables.\n\n## Backup (On-Demand)\n\nA one-time ad-hoc backup uses the standard CNPG `Backup` resource with `method: plugin`:\n\n```yaml\napiVersion: postgresql.cnpg.io/v1\nkind: Backup\nmetadata:\n  name: logical-manual-backup\nspec:\n  method: plugin\n  pluginConfiguration:\n    name: pgdump-backup.cloudnative-pg.io\n    parameters:\n      target_type: s3\n      backup_user: app\n      skip_inaccessible_databases: \"false\"\n      bucket: team-backups\n      path: logical\n      retention_days: \"30\"\n      endpoint_url_secret_name: logical-backup-s3\n      region_secret_name: logical-backup-s3\n      access_key_id_secret_name: logical-backup-s3\n      secret_access_key_secret_name: logical-backup-s3\n  cluster:\n    name: my-app-db\n```\n\nThe S3 secret and cluster plugin config from the quickstart apply here as well.\n\n## ScheduledBackup\n\nFor recurring backups use the standard CNPG `ScheduledBackup` with `method: plugin`:\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: logical-backup-s3\ntype: Opaque\nstringData:\n  endpoint: https://minio.platform.svc:9000\n  access-key-id: minio\n  secret-access-key: minio123\n  region: us-east-1\n---\napiVersion: postgresql.cnpg.io/v1\nkind: ScheduledBackup\nmetadata:\n  name: logical-daily-backup\nspec:\n  schedule: \"0 0 2 * * *\"\n  method: plugin\n  pluginConfiguration:\n    name: pgdump-backup.cloudnative-pg.io\n    parameters:\n      target_type: s3\n      backup_user: app\n      skip_inaccessible_databases: \"false\"\n      bucket: team-backups\n      path: logical\n      object_key_template: \"{namespace}/{cluster}/{database}/{backup_id}.dump\"\n      retention_days: \"30\"\n      endpoint_url_secret_name: logical-backup-s3\n      endpoint_url_secret_key: endpoint\n      region_secret_name: logical-backup-s3\n      region_secret_key: region\n      access_key_id_secret_name: logical-backup-s3\n      access_key_id_secret_key: access-key-id\n      secret_access_key_secret_name: logical-backup-s3\n      secret_access_key_secret_key: secret-access-key\n  cluster:\n    name: my-app-db\n```\n\nSecret ref parameter defaults:\n\n- `endpoint_url_secret_key`: `endpoint`\n- `region_secret_key`: `region`\n- `access_key_id_secret_key`: `access-key-id`\n- `secret_access_key_secret_key`: `secret-access-key`\n\nThe bucket can be set directly via `bucket` or read from a Kubernetes Secret via `bucket_secret_name` / `bucket_secret_key` (default key: `bucket`). This is useful for tools like [s3ops](https://github.com/odit-services/s3ops) that manage per-service S3 configuration. When both are specified, the secret value takes precedence.\n\n`backup_user` controls which PostgreSQL role runs database discovery and `pg_dump`. It defaults to `app`. The plugin reads credentials from CNPG-style Kubernetes Secrets: `app` uses `\u003ccluster\u003e-app`, `postgres` uses `\u003ccluster\u003e-superuser` and requires `spec.enableSuperuserAccess: true`, and any other value uses `\u003ccluster\u003e-\u003cbackup_user\u003e`.\n\n`skip_inaccessible_databases` controls database discovery when the backup user cannot connect to every database. It defaults to `false`, so the plugin attempts every non-template database that allows connections and fails clearly if `pg_dump` lacks permission. Set it to `\"true\"` to skip databases where the backup user lacks `CONNECT` privilege.\n\n`path` and `object_key_template` are configured per `ScheduledBackup`, so each CNPG cluster can use its own bucket or object layout. `path` is an optional prefix. `object_key_template` defaults to `{namespace}/{cluster}/{database}/{backup_id}.dump` and supports these placeholders:\n\n- `{namespace}`\n- `{cluster}`\n- `{database}`\n- `{backup_id}`\n- `{timestamp}`\n\nThe template must include `{database}` and `{backup_id}` to avoid overwriting dumps. For a bucket dedicated to one CNPG cluster, a compact layout can be:\n\n```yaml\nparameters:\n  bucket: my-app-db-backups\n  path: logical\n  object_key_template: \"{database}/{timestamp}/{backup_id}.dump\"\n```\n\nObject keys are written as:\n\n```text\n\u003cpath\u003e/\u003crendered-object-key-template\u003e\n```\n\n## Notes\n\n- The plugin connects through the CNPG read service: `\u003ccluster\u003e-r.\u003cnamespace\u003e.svc:5432`.\n- Credentials are read from the secret selected by `backup_user`; by default this is the CNPG application secret: `\u003ccluster\u003e-app`.\n- Databases are discovered with `SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate` by default. With `skip_inaccessible_databases: \"true\"`, discovery adds `has_database_privilege(datname, 'CONNECT')` so databases the configured role cannot connect to are skipped.\n- Retention deletes objects whose backup timestamp in the filename is older than `retention_days`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodit-services%2Fcnpg-plugin-pgdump","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fodit-services%2Fcnpg-plugin-pgdump","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodit-services%2Fcnpg-plugin-pgdump/lists"}