Database backups before changes

Before you upgrade a database, change its config or move it, take a dump you know you can restore. It takes a minute or two for most small databases, and it's the one backup a disk snapshot can't replace.

Why a dump, not just a snapshot

A disk snapshot of a running server can catch a database halfway through writing. It usually recovers, but "usually" isn't what you want before an upgrade. A dump made by the database's own tool is consistent by design, it's small enough to copy off the server, and you can restore it into a different version or a different machine.

Take one right before the change, even if you have nightly backups. Last night's backup doesn't have this morning's data.

PostgreSQL

Use the custom format. It's compressed by default, and pg_restore can restore all of it or pick out single tables. pg_dump takes a consistent snapshot and doesn't block other users while it runs.

sudo -u postgres pg_dump -Fc -f /var/backups/appdb-$(date +%F-%H%M).dump appdb

pg_dump only dumps one database. Roles (users and their passwords) and tablespaces belong to the whole cluster, so dump them separately with pg_dumpall, which needs a superuser:

sudo -u postgres pg_dumpall --globals-only -f /var/backups/pg-globals-$(date +%F-%H%M).sql

The globals file is plain SQL, restored with psql. Without it, restoring on a fresh server gives errors for every owner and grant that names a role that doesn't exist there yet.

MySQL and MariaDB

For InnoDB tables, --single-transaction dumps a consistent state of the database without blocking applications. Without it, mysqldump locks tables while it reads them.

mysqldump --single-transaction --quick --routines --events \
  appdb > /var/backups/appdb-$(date +%F-%H%M).sql

These options are from the MySQL 8.4 manual. If you run MariaDB, check its own mysqldump documentation.

Redis

With RDB persistence on, Redis writes its data to a single file, dump.rdb by default. The file is never modified once written: Redis writes a new one under a temporary name and renames it into place when it's complete. So copying it while Redis runs is safe. Ask for a fresh one first:

redis-cli LASTSAVE                 # unix time of the last successful save
redis-cli BGSAVE                   # start a new one in the background
redis-cli LASTSAVE                 # repeat until the number changes: that save succeeded
redis-cli CONFIG GET dir           # where the file is
redis-cli CONFIG GET dbfilename
sudo cp -a /var/lib/redis/dump.rdb /var/backups/redis-$(date +%F-%H%M).rdb

Use the path that CONFIG GET dir printed; /var/lib/redis is only the usual location for Debian and Ubuntu packages. If Redis requires a password, redis-cli needs it too.

If you run AOF only (no RDB), the Redis 7 and later AOF is several files in the appenddirname directory, and copying them during a rewrite can give you a broken backup. The Redis docs describe turning off automatic rewrites with CONFIG SET auto-aof-rewrite-percentage 0 while you copy, then setting it back.

MongoDB

mongodump can write everything to one compressed archive file, which is easier to move and keep than a directory of BSON files:

mongodump --uri="mongodb://localhost:27017" --username=backupUser \
  --authenticationDatabase=admin --gzip \
  --archive=/var/backups/mongo-$(date +%F-%H%M).archive.gz

Leave out --password and mongodump asks for it, which keeps it out of your shell history and the process list. On a replica set, add --oplog for a dump that reflects a single moment in time; it can't be combined with --db. Sharded clusters need more care; follow MongoDB's sharded cluster backup tutorial.

Where to keep the dumps

A dump is only a backup once you've restored it. A file that exists isn't proof. An empty dump, a dump of the wrong database, or one cut short by a full disk all look fine until you need them.

Check that a dump restores

Restore into a new, empty database, never over the live one:

# PostgreSQL: list the contents, then restore into a scratch database
pg_restore --list /var/backups/appdb-2026-10-30-1400.dump | head
sudo -u postgres createdb -T template0 appdb_restoretest
sudo -u postgres pg_restore -d appdb_restoretest /var/backups/appdb-2026-10-30-1400.dump

# MySQL: load into a scratch database
mysql -e "CREATE DATABASE appdb_restoretest"
mysql appdb_restoretest < /var/backups/appdb-2026-10-30-1400.sql

# MongoDB: a dry run prints the restore summary without importing anything
mongorestore --gzip --archive=/var/backups/mongo-2026-10-30-1400.archive.gz --dryRun --verbose

Then count rows in a couple of tables you know, compare with the live database, and drop the test database. For Redis, start a second instance on another port with a copy of the RDB file in its dir, and compare DBSIZE. If your database is listening on the internet while you do any of this, fix that first: see Postgres exposed to the internet.

Stenvakt's database guard looks at every database it finds. It flags databases exposed to the internet and risky settings, and gives you a Back up now button before you change anything. Fixes run in safe mode, and you see each one in a Preview first. It's a Mac app and isn't out yet: get early access.

Sources

  1. PostgreSQL docs: pg_dump
  2. PostgreSQL docs: pg_restore
  3. MySQL 8.4 Reference Manual: mysqldump
  4. Redis docs: persistence and backing up Redis data
  5. MongoDB Database Tools: mongodump
Next guideSnapshot before you change things