Backups
Automated volume backups with Restic — hot and cold modes, scheduling, retention, and one-click restore
Overview
PodWarden's backup system creates incremental, deduplicated, encrypted backups of workload volumes using Restic. Backups are orchestrated by the PodWarden Backup Operator — a Kubernetes operator that runs inside your cluster and manages the full backup lifecycle.
What gets backed up: persistent volume contents (PVC data). The backup engine mounts the same volumes as your workload and streams them to a storage connection you control.
Where backups go: any NFS or S3-compatible storage connection registered in PodWarden. One storage connection can serve as the backup target for many workloads. Each policy gets its own isolated Restic repository path.
Prerequisites
The PodWarden Backup Operator must be installed in your cluster before backup policies can be used. Without it, policies can be created but will remain in Pending state and no backups will run.
Install via the Hub catalog or by applying the operator manifest directly:
kubectl apply -f https://www.podwarden.com/operators/backup-operator/install.yamlSee System Apps for more details on operator installation and detection.
How It Works
PodWarden creates BackupPolicy custom resources in your cluster. The backup operator watches for these resources and:
- Schedules backup runs according to the cron expression in the policy
- Creates BackupRun resources for each execution
- Runs Restic inside the cluster to copy volume data to the storage target
- Applies retention rules and updates snapshot metadata
You do not need to manually apply or remove any Kubernetes resources — the operator handles scheduling automatically when a policy is created or updated.
Backup Modes
| Mode | Downtime | When to use |
|---|---|---|
| Hot | None | Workloads that tolerate reading files while running. Optional pre-backup hook for database dumps. |
| Cold | ~30s | Databases or apps that need a consistent snapshot. Workload is scaled to 0 during backup. |
Hot mode
The backup Job mounts the PVC volumes while the workload is running. Add a pre-backup hook to run a command inside the pod before file copy starts:
mysqldump -u root -p$MYSQL_ROOT_PASSWORD --all-databases > /var/lib/mysql/dump.sqlThe hook runs via kubectl exec. File backup starts only after the hook completes.
Cold mode
- PodWarden scales the workload to 0 replicas
- Restic backs up the volumes
- Workload scales back to 1
Typical downtime is 20-60 seconds depending on backup storage speed.
PostgreSQL Backups
In addition to volume backups, PodWarden supports direct PostgreSQL database backups. Instead of mounting PVC files, a PostgreSQL backup runs pg_dump against the database and stores the dump file via Restic.
When to use
- Volume backup with pre-hook — best for most databases. The hook runs
pg_dumpinside the running pod, then Restic copies the dump file along with other volume data. - PostgreSQL backup — use when the database is external to K8s (e.g. a managed database or a database on another host), or when you want a dedicated backup pipeline separate from volume data.
Creating a PostgreSQL Backup Policy
- Go to Backups and click New backup policy
- Set Backup type to
postgres - Fill in the database connection details:
- Host — database hostname or IP
- Port — default 5432
- Database — database name
- User — PostgreSQL user
- Password — stored as a Kubernetes Secret
- Set the schedule and retention as usual
- Select a storage target
- Click Create policy
How it works
The operator creates a BackupRun with two containers:
- Init container (
postgres:16-alpine) — runspg_dump --format=customto a shared temporary volume - Main container (
restic/restic) — uploads the dump to the Restic repository
The custom format produces a single compressed file suitable for selective restore with pg_restore.
Creating a Backup Policy
- Go to Backups in the sidebar
- Click New backup policy
- Select the deployed workload to back up
- Choose backup mode: Hot or Cold
- If hot mode: optionally enter a pre-backup hook command
- Set the schedule (daily, weekly, or custom cron)
- Select the storage target (a registered storage connection)
- Configure retention
- Click Create policy
The operator picks up the new BackupPolicy and begins scheduling runs automatically. Click Run now to trigger an immediate backup outside the normal schedule.
Schedule
| Expression | Meaning |
|---|---|
0 2 * * * | Daily at 2:00 AM (default) |
0 2 * * 0 | Weekly on Sunday at 2:00 AM |
0 * * * * | Every hour |
Retention
Retention rules are passed to Restic's forget command after each backup:
| Setting | Default | Description |
|---|---|---|
| Keep last | 7 | Always keep the N most recent snapshots |
| Keep daily | 7 | Keep one snapshot per day for N days |
| Keep weekly | 4 | Keep one snapshot per week for N weeks |
Restic applies them cumulatively — a snapshot is kept if it matches any rule.
Snapshots
Click any policy row to see its snapshots. Each shows the Restic snapshot ID, timestamp, size, status, and file counts.
Restic's incremental deduplication means subsequent backups of unchanged data are fast and store only deltas.
Restoring from a Snapshot
- Click the restore button on a successful snapshot
- Confirm the restore
- PodWarden creates a RestoreRun CRD; the operator scales the workload to 0, runs
restic restore, then scales back to 1
Restore takes 30-120 seconds plus data transfer time. Data written after the snapshot is overwritten.
The operator performs a compatibility check before restoring — verifying that the target PVC names and sizes are compatible with the snapshot. If the check fails, the RestoreRun is marked Failed with a reason.
Pre-backup Hooks
Shell commands run inside the running pod before Restic copies files. Hot mode only.
# MySQL
mysqldump -u root -p$MYSQL_ROOT_PASSWORD --all-databases > /var/lib/mysql/dump.sql
# PostgreSQL
pg_dumpall -U postgres > /var/lib/postgresql/data/dump.sql
# Redis
redis-cli BGSAVE && sleep 2If the hook exits non-zero, the backup is aborted. Hub templates may pre-fill hooks via backup_hooks metadata.
Storage Requirements
Both NFS and S3-compatible storage connections are supported:
- NFS — on-premises or LAN backups, fast, no egress cost
- S3 — off-site backups, supports AWS S3, MinIO, SeaweedFS, Backblaze B2
Each policy creates its own Restic repository at a unique path:
backups/{cluster}/{workload}-{suffix}/The unique suffix ensures that deleting and recreating a policy for the same workload produces a fresh Restic repository, avoiding password mismatches with leftover encrypted data. Repositories are initialized automatically with a random password stored as a Kubernetes Secret.
Compose Stack Backups
Compose stacks with multiple shared volumes are handled automatically:
- Per-PVC runs — each PVC is backed up by its own BackupRun. Required because local-path volumes are pinned to specific nodes, and different PVCs may live on different nodes.
- Staggered schedules — runs are staggered by 2 minutes. The first run initializes the Restic repository; subsequent runs wait for the repo before starting.
- Single snapshot — per-PVC runs are grouped into one logical snapshot, marked successful only when all runs complete.
No special configuration is needed. PodWarden discovers all PVCs and the operator creates per-PVC runs when the policy is scheduled.
Kubernetes Resources
Each backup policy creates:
- BackupPolicy
pw-backup-{workload}— the CRD watched by the operator. - BackupRun(s) — created by the operator for each scheduled or on-demand execution.
- Secret
pw-backup-{suffix}— Restic password, generated once at policy creation. - Secret for S3 credentials (if applicable) from the storage connection.
- ServiceAccount
podwarden-backupwith RBAC for Deployment reads and pod exec.
Troubleshooting
Policy stuck in Pending: The backup operator may not be installed. Run kubectl get backuppolicies to confirm the CRD exists. See System Apps for installation instructions.
Stuck in running: BackupRuns missing from K8s after 15 minutes are marked failed. Click refresh to sync. Check operator logs: kubectl -n podwarden-system logs deployment/backup-operator.
Inspecting policies and runs:
kubectl get backuppolicies
kubectl get backupruns
kubectl describe backuppolicy <name>
kubectl describe backuprun <name>Pre-backup hook fails: Verify the command and that the pod has the required tools.
Restore fails: Check namespace and PVC names. Inspect the RestoreRun: kubectl describe restorerun <name>.
Password mismatch / ciphertext verification failed: The repo was initialized with a different password. Delete the policy, create a new one (fresh repo path and password), and run a backup.