A Kubernetes-native operator that backs up databases running in your cluster to S3-compatible storage — and restores them on demand.
The operator is built around a pluggable engine model: it handles the generic backup lifecycle (scheduling, job orchestration, retries, state tracking), while small, swappable engine container images handle the database-specific mechanics (dumping and restoring one database type each).
Currently supported engines: PostgreSQL (pg_dump / pg_restore)
Adding a new database type (MongoDB, MySQL, Milvus, Weaviate, …) is designed to require only a new engine image and a registry entry — no changes to the CRDs or controllers.
- You create
DatabaseInstance(how to connect),BackupConfig(where to store), and either aBackup(one-shot) orBackupSchedule(cron) resource. - The operator validates the references, resolves the engine type via the Engine Registry, and creates a Kubernetes Job.
- The Job runs an engine container image with a standardized set of environment variables and mounted secrets.
- The engine connects to the database, dumps it, uploads the dump + manifest to S3, and writes a standardized JSON result to stdout.
- The operator reads the Job's result and exit code, then updates the
Backup/Restorestatus — retrying with backoff on connectivity/storage failures.
| CRD | Purpose |
|---|---|
DatabaseInstance |
Connection details for a database: engineType, host/port, database name, auth SecretRef, and an engine-specific engineConfig map. |
BackupConfig |
S3 destination (endpoint, bucket, prefix, credentials via SecretRef), retention policy, and Job timeout. |
Backup |
A one-shot backup request referencing a DatabaseInstance and BackupConfig. |
BackupSchedule |
Cron-based generator of Backup objects (Kubernetes CronJob → Job pattern, but for backups). |
Restore |
Restores a backup (from a Backup object or a raw S3 path) into a target DatabaseInstance. Validates that source and target engine types match. |
Credentials are never stored in CRD specs — database and S3 credentials are always referenced via Kubernetes Secrets.
The operator and engine images communicate through a fixed contract (defined in pkg/engine/contract.go):
- Environment variables —
OPERATION(backup/restore),DB_HOST,DB_PORT,DB_NAME,DB_USER,S3_ENDPOINT,S3_BUCKET,S3_PREFIX,BACKUP_ID, plus engine-specific ones likeDUMP_FORMAT. - Secrets mounted as files —
/etc/db-secret/password,/etc/s3-secret/access-key,/etc/s3-secret/secret-key. - Exit codes —
0success,1internal error,2auth failure,3connectivity failure (retried),4storage failure (retried). - Result JSON — wrapped in
___RESULT_START___/___RESULT_END___markers on stdout, carrying status, timestamps, size, SHA-256 checksum, S3 object keys, and table count.
Retries: max 3 attempts with 30s / 2m / 5m backoff for connectivity and storage failures.
Backups are stored under a predictable S3 layout, with a self-describing manifest so backups remain discoverable and restorable even if the Backup CRD is deleted:
s3://{bucket}/{prefix}/backups/{engineType}/{databaseName}/{backupId}/
├── dump.{format}
└── backup-manifest.json
docker-compose.yml spins up PostgreSQL 16 and MinIO (S3-compatible) with a pre-created db-backups bucket:
docker compose up -dmake build # build the operator binary
make test # run Go tests
make docker-build # build operator + pg-engine imagesInstall via the Helm chart (charts/kube-db-backup/), which packages the CRDs, operator Deployment, RBAC, and sample application resources:
helm install kube-db-backup charts/kube-db-backup --create-namespace -n db-backupConfigure database connections, S3 credentials, schedules, and images in the chart's values.yaml (or the corresponding manifests under config/). The operator image is overridable via IMG, and the engine images via ENGINE_PG_IMG and ENGINE_MYSQL_IMG (or the PG_ENGINE_IMAGE / MYSQL_ENGINE_IMAGE environment variables on the operator Deployment).
The examples/ directory contains standalone manifests to test each CRD type without re-installing the chart:
# One-shot backups
kubectl apply -f examples/postgres-backup.yaml
kubectl apply -f examples/mysql-backup.yaml
# Scheduled backups
kubectl apply -f examples/postgres-schedule.yaml
kubectl apply -f examples/mysql-schedule.yaml
# Restore into the same instance
kubectl apply -f examples/postgres-restore.yaml
kubectl apply -f examples/mysql-restore.yaml
# Restore into a different target
kubectl apply -f examples/postgres-target.yaml
kubectl apply -f examples/postgres-restore-to-new-target.yaml
# Restore from a raw S3 path (no Backup CRD needed)
kubectl apply -f examples/postgres-restore-from-s3.yamlWatch progress:
kubectl get backups,restores,backupschedules -n db-backup -w- Build a new engine image that implements the engine contract (see
engines/postgres/for a reference implementation). - Add an entry to the Engine Registry in
pkg/engine/registry.go. - Done — no CRD or controller changes.
api/v1alpha1/ # CRD Go types (DatabaseInstance, BackupConfig, Backup, BackupSchedule, Restore)
cmd/ # operator entrypoint (controller-runtime manager)
internal/controller/ # Backup, BackupSchedule, and Restore reconcilers
pkg/engine/ # engine contract + engine registry
engines/postgres/ # the PostgreSQL engine image (backup.sh, restore.sh, entrypoint.sh)
config/ # raw Kubernetes manifests (CRDs, RBAC, manager, samples)
charts/kube-db-backup/ # Helm chart
docker-compose.yml # local dev stack (Postgres + MinIO)
- ARCHITECTURE.md — component design and rationale
- PLAN.md — roadmap and engine abstraction strategy
- REBUILD.md — how to recreate the project from scratch
- Phase 1 (current): PostgreSQL engine, all 5 CRDs, Job-based contract end-to-end.
- Phase 2:
BackupEngineCRD for runtime engine registration; PITR-capable Postgres engine. - Phase 3+: MongoDB, MySQL, then Milvus/Weaviate engines — validating that new databases require only a new image and a registry entry.