Website Backup on VPS: How to Set Up Backups and Verify Them

On shared hosting, backups are handled by the provider. On a VPS, you handle them yourself. That is the main thing that changes after migration, and it is usually remembered at the worst possible moment: after a failed update, a hack, or an accidental DROP TABLE.
The good news is that setting up backups is about an hour of work done once. Below is a ready-made scheme: what to back up, where to store it, how to automate it, and how to make sure the backup actually works.
What exactly needs to be backed up
Website files, including code, uploaded images, and documents. The database, where all content, orders, and users live. Server configuration, including nginx files, certificates, cron jobs, and systemd unit files: without these, recovery turns into reconfiguration from scratch.
The three-copy rule applies even to a small website: the server itself, a copy on another medium, and a copy in a physically different location. A copy stored on the same server saves you from a code error but not from losing the server entirely.
Step 1. The server where this is done
If you are just migrating, the process is as follows: in the NodexGo control panel, click Create Server, choose a location (your real ping is shown next to each one), a plan, an Ubuntu 24.04 image, and a name, then select a billing period of 1, 3, 6, or 12 months. Within a minute, the IP address and root password will appear in the panel and be sent to your email, and you can connect:
ssh root@server-IP-addressAlso consider disk space: backups take up room, and keeping a month of daily database copies directly on the server is common practice. If space runs out, you can always upgrade the plan using the Increase Resources button, and your data stays in place.
NVMe drives and plans with room for backups. Server ready in a minute.
Create a serverStep 2. Database backup
You cannot copy a database like a regular file on a running server, as the result will be a corrupted copy. For MySQL and MariaDB, use mysqldump: the --single-transaction flag takes a consistent snapshot without locking the site.
mysqldump -u root -p --single-transaction --routines --events database_name > /root/backups/db-$(date +%F).sqlFor PostgreSQL, the same is done with pg_dump:
pg_dump -U postgres database_name > /root/backups/db-$(date +%F).sqlDumps compress very well, typically by a factor of five to ten:
gzip /root/backups/db-$(date +%F).sqlStep 3. File backup
Files are conveniently packed into a single archive together with the configuration:
mkdir -p /root/backups
tar -czf /root/backups/files-$(date +%F).tar.gz /var/www /etc/nginx /etc/letsencryptIf there are many files that change rarely, rsync is a better choice than an archive: it copies only what has changed and runs much faster.
rsync -a --delete /var/www/ /root/backups/www-mirror/Step 4. Automation with a single script
Manual backups do not work in practice because people stop doing them after a week. Let us put everything into one script:
nano /root/backup.sh#!/bin/bash
set -euo pipefail
DATE=$(date +%F)
DEST=/root/backups
KEEP_DAYS=14
mkdir -p "$DEST"
# database
mysqldump -u root --single-transaction --routines database_name | gzip > "$DEST/db-$DATE.sql.gz"
# files and configuration
tar -czf "$DEST/files-$DATE.tar.gz" /var/www /etc/nginx /etc/letsencrypt
# remove old backups
find "$DEST" -type f -mtime +$KEEP_DAYS -delete
echo "backup $DATE ok"To prevent the script from prompting for a database password, store it in the file /root/.my.cnf, accessible only by root:
printf '[client]\nuser=root\npassword=database-password\n' > /root/.my.cnf
chmod 600 /root/.my.cnfMake the script executable and test it manually:
chmod +x /root/backup.sh
/root/backup.sh
ls -lh /root/backupsStep 5. Scheduled execution
Open the scheduler and add a daily run at 4 AM with logging:
crontab -e0 4 * * * /root/backup.sh >> /var/log/backup.log 2>&1After a day, confirm that the job ran successfully:
tail -20 /var/log/backup.logStep 6. Off-server backup copy
This is the most important step and the one most often skipped. As long as copies reside on the same disk, they will disappear along with it. The simplest option is to pull them to a home computer or a second server on a schedule:
rsync -avz -e ssh root@server-IP-address:/root/backups/ ~/site-backups/A more robust approach is rclone: it can upload to S3-compatible storage and cloud drives. Configuration is done once through an interactive dialog:
apt install -y rclone
rclone configAfter that, sending copies is added to the same script with a single line:
rclone copy /root/backups remote:site-backups --max-age 2dStep 7. Verify that the backup works
A backup that has never been restored is not really a backup. Verification takes five minutes. Check that the archive is intact and that the dump is not empty:
gzip -t /root/backups/db-*.sql.gz && echo 'archive is intact'
zcat /root/backups/db-2026-08-01.sql.gz | head -20A real verification means restoring the copy to a separate database and inspecting it:
mysql -u root -e 'CREATE DATABASE restore_test'
zcat /root/backups/db-2026-08-01.sql.gz | mysql -u root restore_test
mysql -u root -e 'SELECT COUNT(*) FROM restore_test.wp_posts'It is a good idea to run this kind of check once a month, and always after any change to the backup script.
How often to back up
Use a simple question as your guide: how much data loss can you tolerate. A blog can survive losing a day of data, but an online store losing a day of orders means lost revenue, so the database there is backed up every few hours and files once a day.
Also take a manual backup before any risky action: updating the CMS, migrating the database, or changing the PHP version. It takes thirty seconds and can save an entire evening.
Recovery: the order of steps
Database first, then files, then configuration. Restore the dump:
zcat /root/backups/db-2026-08-01.sql.gz | mysql -u root database_nameRestore the files:
tar -xzf /root/backups/files-2026-08-01.tar.gz -C /If the server fails to boot or SSH access is lost, the server card in the NodexGo panel includes a VNC web console that lets you log into the system without a network connection. As a last resort, there is a Reinstall OS button next to it: a clean system is installed in a couple of minutes, and the site is brought back up from backups using this same guide.
In brief
Back up the database, files, and configuration. Combine everything into one script. Run it daily via cron. Store copies in at least two places, one of which is off the server. Verify by restoring once a month. Backups set up once take about an hour and pay for themselves on the very first bad day.
Need a second server for storing backups? The entry-level plan is more than enough for that.
View plans