PostgreSQL logical backups (pg_dump) with a DB-configurable schedule, count-based retention, SHA-256 checksums, restore, and admin failure alerts.
| File | Purpose |
|---|---|
backup.service.ts |
Create / list / restore / download, scheduler, retention |
dto/backup.dto.ts |
UpdateBackupScheduleDto |
backup.module.ts |
No controller. Routes live in the admin module |
Exposed through /admin/backups/*: list, status, get/update schedule, run now, restore, download.
ensureNoActiveJobs()rejects the request if another backup or restore isRUNNING.- A
DatabaseBackuprow is created (RUNNING). pg_dump --clean --if-exists --no-owner --no-privileges --format=plain --file=<path>runs againstDATABASE_URL(viaexecFile, not a shell).- A SHA-256 checksum and the file size are stored, and the status becomes
COMPLETED. - Retention is enforced (see below).
On failure the row becomes FAILED and admins are notified.
- Stored in the
BackupScheduleConfigtable (single row,DEFAULT_SCHEDULE_ID), created on startup with defaults: disabled, cron0 2 * * *(02:00 daily),retentionCount: 10. PUT /admin/backups/schedulevalidates the cron expression and re-registers the job in-process (CronJobfrom thecronpackage, not@nestjs/schedule).- Scheduled runs retry up to 3 times, waiting 5 minutes between attempts. Admins get a notification after each failed attempt (
retryStatus: retrying, thenexhausted).
Count-based, not age-based: after every successful backup, all COMPLETED backups beyond the newest retentionCount are deleted, both the file on disk and the DB row. Manual and scheduled backups count toward the same limit.
POST /admin/backups/:id/restore runs psql --single-transaction --file <backup> against DATABASE_URL.
- The dump was made with
--clean --if-exists, so restore drops and recreates objects. All data written since the backup is lost. --single-transactionmeans a failed restore rolls back entirely.- Restore status (
restoreStatus,restoredAt,restoredById,restoreError) is recorded on the backup row.
| Variable | Default | Purpose |
|---|---|---|
DATABASE_URL |
required | Source/target database |
BACKUP_STORAGE_PATH |
<cwd>/backups |
Where .sql files are written |
PG_DUMP_PATH |
pg_dump |
Binary used for backups |
PSQL_PATH |
psql |
Binary used for restores |
- Only admins can trigger, restore or download backups (
/adminisADMIN-only and audited). - Backup files contain the entire database, including password hashes and PII. Put
BACKUP_STORAGE_PATHon encrypted, access-controlled storage. DATABASE_URLis passed as a CLI argument and can be visible in the process list (ps) while the dump runs.
pg_dump/psqlmust be installed in the runtime image, and their major version must be ≥ the server's.- Files live on local disk by default. In containers they disappear on redeploy unless
BACKUP_STORAGE_PATHis a mounted volume. - The schedule timer lives in the process. With multiple replicas, every replica runs the scheduled backup. Enable scheduling on one instance only, or add a distributed lock (
CacheService.setNx). - The retry loop blocks that cron tick for up to ~10 minutes.
backup.module.tsand the service use// @ts-nocheck.
backup.service.spec.ts and test/backup/.