Schedule PostgreSQL Backups with cron
Learn to schedule PostgreSQL backups using cron and pg_dump. This tutorial covers the core concepts, step-by-step implementation, practical examples, and troubleshooting tips to automate your database backups effectively.
Focus: schedule backups with cron and pg_dump
You've spent weeks perfecting your PostgreSQL schema, and the data inside it is irreplaceable—yet without automated backups, one accidental DROP TABLE or a corrupted disk can erase months of work in seconds. Manually running pg_dump whenever you remember is not a backup strategy; it's a gamble. This lesson shows you how to schedule backups with cron and pg_dump, turning a fragile hope into a reliable, unattended process that runs every night while you sleep.
The problem this lesson solves
Database backups are like insurance—you don't appreciate them until disaster strikes. A hard drive fails, a misapplied migration deletes a critical table, or a script overwrites production data with test data. Without a recent, restorable backup, recovery means rebuilding from logs, backups from a colleague's laptop, or simply accepting the loss.
Manual backups are worse than none because they give you a false sense of security. You think you're covered because you ran pg_dump last Tuesday, but the backup file sits on the same server that just caught fire. And even if it survived, you'd have to remember the exact command, the file location, and the restoration procedure—under pressure.
The solution is automation: let the operating system's task scheduler (cron) run pg_dump on a regular schedule. This lesson gives you a battle-tested pattern that's simple enough for a single-node app and scalable enough for multi-database setups.
Core concept / mental model
Think of pg_dump as a snapshot camera for your database. It takes a consistent picture of the data and schema at a point in time and writes it to a file. Cron is the alarm clock that wakes up the camera every day at a set time, takes the photo, and files it in an archive.
The magic is in the combination:
pg_dumphandles the what — which database, what format, what data to include.cronhandles the when — the schedule, the repetition, the log output.
A typical backup job looks like this in your mind's eye:
cron (every day at 02:00)
└─ runs a shell script (backup.sh)
└─ calls pg_dump → compressed .dump file
└─ timestamps the file (backup_2025-04-01.dump)
└─ cleans up old backups
The key mental shift: you're not writing a command anymore; you're writing a policy. The policy defines when to snapshot, where to store it, and how long to keep it. Cron enforces the policy without human intervention.
How it works step by step
Setting up scheduled PostgreSQL backups involves four logical steps that build on each other. Follow them in order to avoid surprises.
-
Verify
pg_dumpis installed and accessible. - Runpg_dump --versionto confirm it's in yourPATH. If not, install it via your package manager or the official PostgreSQL installer. - This is your safety check — a missing executable will silently fail inside cron. -
Confirm PostgreSQL authentication works non-interactively. - Cron runs with a minimal environment, so your interactive password prompt won't work. - Set up a
.pgpassfile with the connection details, or use peer/ident authentication where the Postgres user matches your system user. - Example.pgpass:localhost:5432:mydb:pguser:secret(chmod 600). -
Write a backup script that calls
pg_dump. - Use a custom-format dump (.dump) for flexibility and compression. - Add a timestamp to the filename so each backup is unique. - Include cleanup logic to remove backups older than N days. -
Schedule the script with cron. - Edit your user crontab with
crontab -e. - Add a line with the schedule (minute, hour, day-of-month, month, day-of-week) plus the full path to your script. - Redirect output to a log file so you can verify cron ran successfully.
# Inside backup.sh
#!/bin/bash
BACKUP_DIR="/var/backups/postgres"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
pg_dump -U pguser -h localhost -Fc mydb > "$BACKUP_DIR/mydb_$TIMESTAMP.dump"
find "$BACKUP_DIR" -name "*.dump" -mtime +7 -delete
# crontab entry (runs every day at 02:00)
0 2 * * * /home/opc/backup.sh >> /var/log/pg_backup.log 2>&1
Hands-on walkthrough
Let's build the backup pipeline end-to-end. We'll assume a PostgreSQL database named mydb running on localhost, and a user pguser.
Step 1: Create the backup script.
Create a file named backup.sh in your home directory with the following content. Make it executable with chmod +x backup.sh.
#!/bin/bash
#!/bin/bash
# PostgreSQL backup with timestamp and rotation
BACKUP_DIR="/var/backups/postgres"
KEEP_DAYS=7
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Run pg_dump (custom format, compressed)
pg_dump -U pguser -h localhost -Fc mydb > "$BACKUP_DIR/mydb_$TIMESTAMP.dump"
# Check exit status
if [ $? -eq 0 ]; then
echo "[$(date)] Backup successful: mydb_$TIMESTAMP.dump"
else
echo "[$(date)] Backup FAILED for mydb"
fi
# Delete backups older than KEEP_DAYS
find "$BACKUP_DIR" -name "*.dump" -mtime +"$KEEP_DAYS" -delete
Step 2: Set up passwordless authentication.
Create ~/.pgpass with the connection details and lock down permissions.
# ~/.pgpass
localhost:5432:mydb:pguser:your_password
chmod 600 ~/.pgpass
Step 3: Test the script manually.
Run the script and verify the output file appears in your backup directory.
./backup.sh
ls -lh /var/backups/postgres/
Expected output:
[2025-04-01 14:30:00] Backup successful: mydb_20250401_143000.dump
-rw-r--r-- 1 opc opc 4.2K Apr 1 14:30 mydb_20250401_143000.dump
Step 4: Add the cron job.
Open your crontab and add the schedule. Use crontab -e.
# m h dom mon dow command
0 2 * * * /home/opc/backup.sh >> /var/log/pg_backup.log 2>&1
Step 5: Verify cron is working.
Check the log after the scheduled time runs, or force a run by executing the script and inspecting the log.
cat /var/log/pg_backup.log
You should see the success message timestamped with the run time.
Compare options / when to choose what
While pg_dump + cron is the most portable and scheduler-agnostic approach, there are alternatives worth knowing. Here's a quick comparison to help you decide.
| Option | Pros | Cons | Best for |
|---|---|---|---|
| cron + pg_dump | Simple, OS-native, no extra tools | No built-in monitoring / retries, separate log management | Small-to-medium databases, single nodes, learning |
| pg_dump with a scheduled task (Windows) | Native on Windows | Different syntax, less familiar to Unix folks | Windows-only environments |
| pgAdmin backup scheduler | GUI, visual schedules | Requires pgAdmin server running, less scriptable | Ad-hoc or GUI-centric teams |
| Barman / pgBackRest | Advanced features: incremental, compression, WAL archiving, monitoring | More setup, steeper learning curve | Production, large databases, need point-in-time recovery |
| Kubernetes CronJob + pg_dump | Declarative, integrates with k8s ecosystem | Requires a running k8s cluster | Cloud-native apps already on k8s |
Pro tip: Start with cron + pg_dump. Once you outgrow it, move to a purpose-built tool like pgBackRest, but the cron pattern remains a solid fallback.
Troubleshooting & edge cases
Cron jobs fail silently more often than they succeed on the first try. Here are the classic pitfalls and how to fix them.
1. "Password authentication failed" or "no password supplied"
Cron's environment doesn't auto-load your shell profile, so PGPASSWORD or .pgpass might be missing.
- Solution: Use
.pgpass(chmod 600) and place it in the cron user's home directory. Or setPGPASSWORDin the script (less secure).
2. "pg_dump: command not found"
pg_dump lives in a directory that isn't in cron's PATH (e.g., /usr/pgsql-15/bin).
- Solution: Use the full path in your script, e.g.,
/usr/pgsql-15/bin/pg_dump.
3. Cron jobs don't run at all
Check the cron service status and your crontab syntax.
systemctl status crond # or cron
crontab -l
Also confirm the script is executable and starts with a shebang (#!/bin/bash).
4. Backup file is 0 bytes
The dump failed during the command, but the file was created. Always check the exit code before celebrating.
- Solution: Use the
if [ $? -eq 0 ]pattern in your script, and log errors.
5. Timezone surprises
Cron uses the system timezone, which may differ from your application's timezone. You can set CRON_TZ at the top of the crontab.
CRON_TZ=America/New_York
0 2 * * * /home/opc/backup.sh
6. Disk fills up due to too many backups
The cleanup command failed or the find pattern is wrong.
- Solution: Test
findwith-printbefore adding-delete. SetKEEP_DAYSlower if needed.
What you learned & what's next
You've learned how to schedule backups with cron and pg_dump — a reliable, automated way to protect your PostgreSQL data. You now understand the core concept (cron as the scheduler, pg_dump as the snapshot tool), the step-by-step setup (script creation, .pgpass configuration, crontab entry), and how to troubleshoot common issues like missing paths and authentication failures.
You've also seen how this pattern compares to more advanced backup tools, giving you a clear decision path for your own environment.
Now that your backups are running on autopilot, the next logical step is to test your restore process. A backup is only as good as its ability to be restored. Check the next lesson in this track to learn how to validate your backups and practice disaster recovery with pg_restore.
Keep your backups scheduled, your data safe, and your confidence high.
Practice recap
Create a backup script for a local testdb and schedule it to run every hour in cron. After the first run, delete a sample table, then restore the dump with pg_restore to confirm your backup actually works. Verify the table is back and the restore log shows no errors.
Common mistakes
- Forgetting to set
PGPASSWORDor.pgpass— cron runs with a non-interactive shell, so your normal auth won't work. - Using a relative path to
pg_dumpinstead of the absolute path (/usr/pgsql-15/bin/pg_dump), causing 'command not found'. - Not checking the exit code of
pg_dump— a failed dump can produce an empty or corrupted file that looks successful. - Skipping the
find -deletecleanup, leading to disk space exhaustion over time. - Testing the script only manually and assuming cron will behave the same — always verify with the cron log.
Variations
- Use
pg_dump --format=plainif you need a plain SQL file for easier cross-version restoration. - Use a systemd timer instead of cron for more predictable scheduling on modern Linux.
- Store backups on a different server or cloud storage (e.g., using
rcloneorscp) to protect against local disk failure.
Real-world use cases
- Automated nightly backups of a production e-commerce database to a local directory, retaining 7 days of snapshots.
- Scheduling weekly off-site backups of a PostgreSQL instance to an S3 bucket via cron and
pg_dump. - Using cron to trigger
pg_dumpbefore a schema migration, ensuring a quick rollback point.
Key takeaways
- cron + pg_dump is a simple, reliable way to automate PostgreSQL backups — no extra tools required.
- Your cron environment is minimal: use absolute paths,
.pgpass, and log output to debug failures. - Always check the exit status of
pg_dumpto catch failures early. - Rotate old backups to prevent disk exhaustion — a
find -mtimecleanup is essential. - Test your backups by restoring them — an untested backup is not a backup.
- For advanced needs (incremental, WAL archiving), graduate to Barman or pgBackRest, but cron remains a solid baseline.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.