A Kubernetes operator that automates PostgreSQL database backups to S3 using CronJobs.
- Scheduled
pg_dumpbackups via CronJobs - Uploads backups to S3 with automatic retention cleanup
- S3 backup cleanup on resource deletion (finalizer-based)
- Structured logging and Prometheus metrics
- Leader election for production HA
| Resource | Action |
|---|---|
PgDump created |
Operator creates a CronJob that runs pg_dump + aws s3 cp |
| Backup runs | Old backups beyond retention count are deleted from S3 |
PgDump deleted |
Finalizer triggers a cleanup Job to delete all S3 backups |
- kubectl version v1.11.3+
- Access to a Kubernetes v1.11.3+ cluster
- An S3 bucket for storing backups
- Kubernetes Secrets for PostgreSQL and AWS credentials
The operator image is available at aayush0325/pgdump-operator:latest on Docker Hub.
1. Install CRDs:
kubectl apply -f https://raw.githubusercontent.com/aayush0325/pgdump-operator/main/config/crd/bases/backup.aayush0325.fun_pgdumps.yaml2. Deploy the operator:
kustomize build "github.com/aayush0325/pgdump-operator/config/default?ref=main" \
| sed 's|image: controller:latest|image: aayush0325/pgdump-operator:latest|' \
| kubectl apply -f -Or, if you prefer a single consolidated manifest:
# Clone the repo and generate the installer YAML
git clone https://github.com/aayush0325/pgdump-operator.git
cd pgdump-operator
make build-installer IMG=aayush0325/pgdump-operator:latest
kubectl apply -f dist/install.yamlIf you want to build and push the image yourself:
Prerequisites: go v1.24.0+, docker 17.03+
1. Install CRDs:
make install2. Build and push the operator image:
make docker-build docker-push IMG=aayush0325/pgdump-operator:latest3. Deploy the operator:
make deploy IMG=aayush0325/pgdump-operator:latestPostgreSQL credentials secret:
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
type: Opaque
stringData:
PGPASSWORD: "your-postgres-password"
PGUSER: "postgres"kubectl apply -f postgres-secret.yamlAWS credentials secret:
apiVersion: v1
kind: Secret
metadata:
name: aws-credentials
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: "your-access-key"
AWS_SECRET_ACCESS_KEY: "your-secret-key"
AWS_DEFAULT_REGION: "us-east-1"kubectl apply -f aws-secret.yamlApply the demo manifest:
kubectl apply -f demo/backup.yamldemo/backup.yaml:
apiVersion: backup.aayush0325.fun/v1alpha1
kind: PgDump
metadata:
name: postgres-daily-backup
spec:
postgres:
host: postgres-service
port: 5432
name: myapp
credentialsSecret: postgres-secret
schedule: "0 2 * * *"
retention: 7
s3bucket:
bucket: my-backups
region: us-east-1
path: backups
credentialsSecret: aws-credentials| Field | Description |
|---|---|
spec.postgres.host |
PostgreSQL host |
spec.postgres.port |
PostgreSQL port |
spec.postgres.name |
Database name |
spec.postgres.credentialsSecret |
Secret containing PGUSER/PGPASSWORD |
spec.schedule |
Cron schedule expression (e.g. 0 2 * * *) |
spec.retention |
Number of backups to keep in S3 |
spec.s3bucket.bucket |
S3 bucket name |
spec.s3bucket.region |
S3 region |
spec.s3bucket.path |
S3 path prefix |
spec.s3bucket.credentialsSecret |
Secret containing AWS credentials |
The operator exposes Prometheus metrics at /metrics:
| Metric | Description |
|---|---|
pgdump_backup_success_total |
Total successful backup reconciliations |
pgdump_backup_failure_total |
Total failed backup reconciliations |
pgdump_cleanup_total |
Total S3 cleanup operations |
1. Delete PgDump resources:
kubectl delete -f demo/backup.yaml2. Delete the controller deployment:
kubectl delete -f dist/install.yaml
# or, if deployed with kustomize:
make undeploy3. Uninstall CRDs:
kubectl delete -f https://raw.githubusercontent.com/aayush0325/pgdump-operator/main/config/crd/bases/backup.aayush0325.fun_pgdumps.yamlCopyright 2026.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.