Nightly PostgreSQL backup script with pg_dump and rotation
A bash script for cron that dumps cluster roles plus every database in custom format into a dated folder, checks each archive, and deletes folders older than N days only after a successful run.
Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?
What this changes
Creates a dated folder in /var/backups/postgresql (BACKUP_DIR) with globals.sql and one .dump per database, then permanently deletes backup folders there older than KEEP_DAYS (default 14). The databases are only read.
How to undo it
Delete a backup with rm -rf /var/backups/postgresql/STAMP. Rotated backups cannot be recovered: copy them elsewhere or raise KEEP_DAYS first, and keep only these backups in BACKUP_DIR.
This script makes a logical backup of a whole PostgreSQL server every night: one globals.sql with roles and tablespaces, and one custom-format .dump per database. Each run writes to a new folder named after the date and time. Old folders are removed only when every database in the current run was dumped successfully, so a broken night never deletes your last good backups.
The script
Check before you run this
- Deletes files and folders without asking. Check the path before running.
#!/usr/bin/env bash
set -Eeuo pipefail
BACKUP_DIR=${BACKUP_DIR:-/var/backups/postgresql}
KEEP_DAYS=${KEEP_DAYS:-14}
export PGHOST=${PGHOST:-/var/run/postgresql}
log() { printf '%s %s\n' "$(date '+%F %T')" "$*" >&2; }
[[ $KEEP_DAYS =~ ^[0-9]+$ ]] || { log "KEEP_DAYS must be a number"; exit 2; }
umask 077
TARGET="$BACKUP_DIR/$(date +%Y-%m-%d_%H%M%S)"
mkdir -p -- "$TARGET"
log "writing to $TARGET"
pg_dumpall --globals-only --file="$TARGET/globals.sql"
mapfile -t DATABASES < <(psql -X -At -v ON_ERROR_STOP=1 -d postgres -c \
"SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate ORDER BY datname")
status=0
for db in "${DATABASES[@]}"; do
log "dumping $db"
if pg_dump --format=custom --file="$TARGET/$db.dump.partial" --dbname="$db" \
&& pg_restore --list "$TARGET/$db.dump.partial" > /dev/null; then
mv -- "$TARGET/$db.dump.partial" "$TARGET/$db.dump"
else
log "FAILED: $db"
rm -f -- "$TARGET/$db.dump.partial"
status=1
fi
done
if (( status != 0 )); then
log "at least one dump failed; old backups were kept"
exit "$status"
fi
touch -- "$TARGET/COMPLETE"
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -name '20*' -mtime +"$KEEP_DAYS" -print |
while IFS= read -r old; do
log "removing old backup $old"
rm -rf -- "$old"
done
log "done: ${#DATABASES[@]} database(s)"
Install it
- Save it as
/usr/local/bin/pg-nightly-dump.shand make it executable withsudo chmod 755 /usr/local/bin/pg-nightly-dump.sh. - Create the backup folder, owned by the
postgresOS user:sudo install -d -o postgres -g postgres -m 700 /var/backups/postgresql. - Run it once by hand as
postgresand check the output folder. - Add it to the
postgresuser’s crontab.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo -u postgres /usr/local/bin/pg-nightly-dump.sh
ls -l /var/backups/postgresql/*/
sudo -u postgres crontab -e
# m h dom mon dow command
30 2 * * * /usr/local/bin/pg-nightly-dump.sh >> /var/log/postgresql/pg-nightly-dump.log 2>&1
How it works
- It connects through the local socket as
postgres, which uses peer authentication on Debian and Ubuntu. If your server uses a different socket directory (check withSHOW unix_socket_directories;), setPGHOSTto it. For a remote server setPGHOST,PGUSERand a~/.pgpassfile. - The database list comes from
pg_database, skipping templates and databases that do not allow connections. - Each dump is written as
.partialand renamed only afterpg_restore --listcould read it, so a half-written file never looks like a good backup. umask 077makes the files readable only by the owner, because dumps contain all your data.KEEP_DAYS(default 14) andBACKUP_DIRcan be set in the environment, for exampleKEEP_DAYS=30in the crontab line.
Rotation deletes folders. Before the first real run, check what would be removed by running the same find command with -print only: find /var/backups/postgresql -mindepth 1 -maxdepth 1 -type d -name '20*' -mtime +14 -print. Do not point BACKUP_DIR at a folder that holds anything else.
Restoring
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo -u postgres psql -X -f /var/backups/postgresql/2026-10-15_023000/globals.sql postgres
sudo -u postgres createdb -T template0 appdb
sudo -u postgres pg_restore -d appdb -j 4 /var/backups/postgresql/2026-10-15_023000/appdb.dump
Copy the backup folder to another machine or to object storage as well; a backup on the same disk as the database does not survive the loss of that disk. For restoring to a moment between two nightly runs you need WAL archiving and physical backups instead.
Official documentation
Did this work for you?
No votes yet. Be the first.
If it did not work, the report form below tells the editors what went wrong.
See also
- 0202 Back up and restore PostgreSQL with pg_dump, pg_restore and pg_dumpall Make logical backups of one database with pg_dump, of roles and the whole cluster with pg_dumpall, and restore them with pg_restore or… Beginner · Step by step · Linux Article Backup & restore 15 Oct 2026
- 0203 Physical backups and point-in-time recovery with pg_basebackup and WAL archiving A base backup plus a continuous archive of WAL files lets you restore a PostgreSQL cluster to any moment after the backup,… Advanced · Explains an idea · Linux Article Backup & restore · Replication & high availability 15 Oct 2026
- 0257 Run PostgreSQL properly on a Linux server: install, lock down, back up, monitor and patch Set up PostgreSQL 18 from the official repository for one application: a dedicated role and database, SCRAM logins from one app server… Intermediate · Guided project · Linux Article Backup & restore · Install & connect · Roles & administration 15 Oct 2026
Repos in the wild
- pgBackRest: backup and restore for PostgreSQL pgbackrest/pgbackrest pgBackRest is a widely used PostgreSQL backup tool with parallel full, differential and incremental backups, WAL archiving, encryption and S3, Azure or GCS repositories. GitHub
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- Dominik +4,273 chars