MySQL Backup and Recovery: mysqldump and Scheduled Backups
Take consistent logical backups with mysqldump and `--single-transaction`, then automate them with cron, gzip, and off-site copies to build a recoverable safety net for your server's databases.
Your database is often the single most irreplaceable thing on a server. You can reinstall the OS and redeploy your app, but lost business data rarely comes back. This guide walks through logical backups with mysqldump on an Ubuntu/Debian server (VPS), and how to put them on autopilot.
Dumping with mysqldump
mysqldump ships with the MySQL/MariaDB client. It writes your tables out as plain SQL, which you replay to restore.
# Dump a single database
mysqldump -u root -p mydb > mydb.sql
# Dump every database
mysqldump -u root -p --all-databases > all.sql
# Dump just one table
mysqldump -u root -p mydb users > users.sql
Add --single-transaction
The danger with live backups is that data keeps changing mid-dump, leaving you with an inconsistent snapshot. For InnoDB tables, --single-transaction reads everything inside one transactional snapshot, giving you a consistent view without locking the tables:
mysqldump -u root -p --single-transaction --quick mydb > mydb.sql
--quick streams rows one at a time instead of buffering a whole table in memory, which helps with large tables. Note that --single-transaction only works for InnoDB; if you still have MyISAM tables, consistency isn't guaranteed.
Restoring data
Restoring simply feeds the SQL back into mysql. Create the target database first:
mysqladmin -u root -p create mydb
mysql -u root -p mydb < mydb.sql
A dump made with --all-databases already contains the CREATE DATABASE statements, so you can pipe it in directly with mysql -u root -p < all.sql.
Compress to save space
SQL text compresses extremely well. Pipe the dump straight through gzip so you never write a huge intermediate file:
mysqldump -u root -p --single-transaction mydb | gzip > mydb.sql.gz
# Decompress and restore in one line
gunzip < mydb.sql.gz | mysql -u root -p mydb
Daily backups with cron
Wrap the command in a script at /usr/local/bin/db-backup.sh that names files by date and prunes anything older than N days:
#!/bin/bash
DIR=/var/backups/mysql
mkdir -p "$DIR"
DATE=$(date +%F)
mysqldump -u root -p'yourpassword' --single-transaction --all-databases \
| gzip > "$DIR/all-$DATE.sql.gz"
# Keep only the last 7 days
find "$DIR" -name '*.sql.gz' -mtime +7 -delete
Make it executable and add it to crontab to run at 3 a.m. every day:
chmod 700 /usr/local/bin/db-backup.sh
crontab -e
# Add this line
0 3 * * * /usr/local/bin/db-backup.sh
Putting a password in the script is risky. A cleaner approach is to store credentials in /.my.cnf (with permissions set to 600) so the script never spells out a plaintext password.
Keep a copy off-site
If your backups live on the same machine as the database, one hardware failure takes out both. Always copy backups elsewhere — push them to another server with rsync or scp, or sync them to object storage:
rsync -avz /var/backups/mysql/ user@backup-host:/backups/db/
A word on physical backups
mysqldump is a logical backup: recovery means replaying SQL, which gets slow for very large datasets in both directions. For those cases, consider a physical backup that copies the data files directly. The open-source Percona XtraBackup supports hot (online) backups of InnoDB with fast backup and restore, which suits databases of tens of gigabytes and up. The trade-off is more setup and less flexibility for major-version migrations than logical dumps offer.
Summary
Good database backups come down to "automated, off-site, and verified": grab a consistent snapshot with mysqldump --single-transaction, pipe it through gzip, run it daily via cron while keeping N days, and rsync it somewhere else. Above all, actually rehearse a restore now and then — a backup you've never tested is no backup at all. Reach for XtraBackup physical backups only once your database outgrows logical dumps.