
September 08, 2026
13 min read
By Kokil Thapa | Last reviewed: September 2026
You lose data the day you skip backups, not the day a disk fails. An automated server backups complete setup turns nightly dumps, file sync, and off-site copies into a routine you never have to remember. I run this pattern on Ubuntu servers that host Laravel and WordPress production stacks, including sister sites on a shared Deployer 7 pipeline. This guide walks through a copy-paste workflow: what to back up, how to schedule it, where to store copies, and how to prove a restore works before you need one.
What should an automated server backups complete setup include?
Think in layers. You need local hot copies for fast rollback, off-site warm copies for server loss, and a written restore path anyone on the team can follow.
A complete stack covers four asset classes on a typical PHP web server:
- Databases — MySQL 9.7 or MariaDB 12.3 dumps for Laravel, WordPress, and custom apps
- Application files — code,
.env, uploaded media, and shared storage outside Git - Server config — Nginx or Apache vhosts, PHP-FPM pools, SSL paths, cron entries
- Secrets inventory — a checklist of what lives only on disk, not in your password manager
On legal-tech portals and booking systems I maintain, uploaded PDFs and client documents often matter more than the database row count. Back up storage/app and any custom upload roots explicitly.
The 3-2-1 backup rule still holds in 2026. Keep three copies of data on two different media with one copy off-site. A single cron job dumping MySQL to the same disk as your database is not a backup strategy—it is a false sense of safety.
Document a one-page runbook. List backup paths, cron schedules, remote credentials location, and who gets alerted on failure. Store that runbook outside the server you are protecting.
How do you configure daily database and file backups on Ubuntu?
Start with a dedicated backup user, a fixed directory layout, and scripts that exit non-zero on failure. Cron only emails you when something breaks if the script fails correctly.
Create the backup directory and user
sudo mkdir -p /var/backups/{mysql,files,logs}
sudo chown -R backup:backup /var/backups
sudo chmod 750 /var/backups Create a backup system user with read access to web roots and MySQL dump privileges. Never run backups as root unless you have no alternative.
MySQL dump script
For MySQL 9.7 on Ubuntu 24.04, use a credentials file instead of putting passwords in cron:
sudo nano /root/.my.cnf.backup [client]
user=backup
password=STRONG_PASSWORD_HERE
host=localhost sudo chmod 600 /root/.my.cnf.backup Grant dump-only access:
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'STRONG_PASSWORD_HERE';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, RELOAD ON *.* TO 'backup'@'localhost';
FLUSH PRIVILEGES; Save the dump script at /usr/local/bin/backup-mysql.sh:
#!/bin/bash
set -euo pipedfail
BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y-%m-%d_%H%M)
LOG="/var/backups/logs/mysql-${DATE}.log"
RETAIN_DAYS=14
mkdir -p "$BACKUP_DIR" /var/backups/logs
{
echo "=== MySQL backup started: $(date) ==="
for DB in $(mysql --defaults-extra-file=/root/.my.cnf.backup -N -e \
"SELECT schema_name FROM information_schema.schemata \
WHERE schema_name NOT IN ('information_schema','performance_schema','sys','mysql');"); do
mysqldump --defaults-extra-file=/root/.my.cnf.backup \
--single-transaction --routines --triggers --events \
"$DB" | gzip > "${BACKUP_DIR}/${DB}_${DATE}.sql.gz"
echo "Dumped: $DB"
done
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +${RETAIN_DAYS} -delete
echo "=== MySQL backup finished: $(date) ==="
} >> "$LOG" 2>&1 sudo chmod 750 /usr/local/bin/backup-mysql.sh
sudo chown root:backup /usr/local/bin/backup-mysql.sh File backup with rsync
Sync application trees and config. Exclude cache and transient paths:
#!/bin/bash
set -euo pipefail
DATE=$(date +%Y-%m-%d)
DEST="/var/backups/files/${DATE}"
LOG="/var/backups/logs/files-${DATE}.log"
APPS="/var/www"
mkdir -p "$DEST" /var/backups/logs
{
echo "=== File backup started: $(date) ==="
rsync -aH --delete \
--exclude 'node_modules/' \
--exclude 'vendor/' \
--exclude 'storage/framework/cache/' \
--exclude 'storage/logs/' \
--exclude 'bootstrap/cache/' \
"${APPS}/" "${DEST}/apps/"
rsync -aH /etc/nginx/ "${DEST}/nginx/" 2>/dev/null || true
rsync -aH /etc/php/ "${DEST}/php/" 2>/dev/null || true
echo "=== File backup finished: $(date) ==="
} >> "$LOG" 2>&1 Save that as /usr/local/bin/backup-files.sh. The rsync and cron automation guide covers incremental flags and bandwidth limits in more depth.
Schedule with cron
sudo crontab -e # Daily MySQL dump at 02:15
15 2 * * * /usr/local/bin/backup-mysql.sh
# Daily file sync at 03:00
0 3 * * * /usr/local/bin/backup-files.sh
# Weekly config tarball Sunday 04:00
0 4 * * 0 tar -czf /var/backups/files/etc-$(date +\%Y-\%m-\%d).tar.gz /etc/nginx /etc/php /etc/cron.d Stagger jobs so a heavy mysqldump does not overlap with rsync on a small VPS. On Rs 1,500/month (~USD 11) entry plans, I/O contention can push backups past the maintenance window.
Which backup tools work best for Laravel and WordPress servers?
Shell scripts plus cron remain the baseline on VPS hosting. Application-level tools add convenience but do not replace off-site copies.
| Tool | Best for | Off-site | Restore speed | Ops overhead |
|---|---|---|---|---|
| Shell + mysqldump + rsync | Any PHP stack, full control | Manual rclone/S3 step | Fast local, medium remote | Low once scripted |
| Spatie Laravel Backup | Laravel 12/13 apps | S3, SFTP built-in | Good with zip bundles | Low in-app config |
| WP-CLI + cron | WordPress 7.1 sites | Needs separate sync | Good per site | Low per install |
| rclone only | File-heavy, no DB logic | Native | Depends on remote | Very low |
For Laravel, I regularly use Spatie Laravel Backup alongside server-level dumps. The package zips database and selected directories, then pushes to S3 or SFTP. Schedule it through the Laravel queue or scheduler so it does not fight with system cron.
Typical config/backup.php source paths for a Laravel 13 app:
'source' => [
'files' => [
'include' => [
base_path(),
],
'exclude' => [
base_path('vendor'),
base_path('node_modules'),
storage_path('logs'),
],
],
'databases' => ['mysql'],
], Run via scheduler in routes/console.php or app/Console/Kernel.php on older apps:
Schedule::command('backup:run --only-db')->daily()->at('01:30');
Schedule::command('backup:clean')->daily()->at('05:00');
Schedule::command('backup:monitor')->daily()->at('06:00'); For WordPress 7.1, WP-CLI backup automation exports the database and syncs wp-content/uploads. WooCommerce 11.1 shops need the uploads tree and any custom order-export folders included.
On shared EC2 infrastructure where several legal-tech sites run the same Deployer 7 workflow, I keep one server-level script as the source of truth. App-level backups are a bonus layer, not the only layer.
Compare transport tools in the rsync vs rclone breakdown if you sync to object storage or a second VPS.
How do you automate off-site backups to S3 or remote storage?
Local archives protect against bad deploys and accidental deletes. They do not protect against datacenter fire, ransomware, or a stolen server image. Push encrypted copies off-site every night.
Install and configure rclone
sudo apt update && sudo apt install -y rclone
rclone config Create a remote named s3-backups pointing at your bucket. Use a dedicated IAM user with write-only access to backups/ prefix. Never reuse production app credentials.
Upload script at /usr/local/bin/backup-offsite.sh:
#!/bin/bash
set -euo pipefail
REMOTE="s3-backups:prod-server-01"
LOCAL="/var/backups"
LOG="/var/backups/logs/offsite-$(date +%Y-%m-%d).log"
{
echo "=== Off-site sync started: $(date) ==="
rclone sync "$LOCAL/mysql" "${REMOTE}/mysql" \
--transfers 4 --checkers 8 \
--exclude "*.tmp"
rclone sync "$LOCAL/files" "${REMOTE}/files" \
--transfers 4 --checkers 8
echo "=== Off-site sync finished: $(date) ==="
} >> "$LOG" 2>&1 Schedule at 04:30, after local jobs finish. The off-site S3 backup guide covers bucket policies, lifecycle rules, and cross-region replication.
Budget roughly Rs 800–2,500/month (~USD 6–19) for S3 storage on a small Laravel plus WordPress server. Exact cost depends on retention and upload volume. Use the JSON formatter to validate IAM policy documents before you apply them.
Encrypt sensitive dumps at rest with GPG before upload if compliance requires it:
gpg --symmetric --cipher-algo AES256 \
/var/backups/mysql/myapp_2026-09-08.sql.gz Store the GPG passphrase in your team vault, not on the server.
Align retention with your cloud disaster recovery plan. Match RPO and RTO targets to how often you sync and how fast you can rebuild a server from scratch.
How do you verify and restore automated server backups?
Untested backups are wishful thinking. Schedule a monthly restore drill to a staging VPS or local Docker MySQL container.
Database restore test
- Download the latest
.sql.gzfrom off-site or copy from local archive - Spin up a throwaway MySQL instance
- Import and run application smoke tests
- Log date, file name, duration, and any errors
gunzip -c /var/backups/mysql/myapp_2026-09-08.sql.gz | mysql -u root -p restore_test For Laravel, run migrations status and hit a health route. For WordPress, load the admin login and check permalinks. On a production Laravel application I have restored this way, the gap between "backup exists" and "backup works" showed up within the first import—missing stored procedures or charset mismatches.
Full server rebuild checklist
When the VPS is gone, you rebuild in this order:
- Provision Ubuntu 24.04 with PHP 8.5 or 8.4 per app requirements
- Restore Nginx and PHP-FPM config from tarball
- Deploy code from Git or restore file archive
- Import latest MySQL dump
- Restore
storage/and upload directories - Reload PHP-FPM, verify SSL with Certbot
- Run queue workers and cron
The Ubuntu PHP server setup guide covers base provisioning. Pair it with your backup runbook so rebuild time is measured, not guessed.
Projects like Notary Kathmandu and other sister sites on shared infrastructure share one backup playbook. That consistency matters when you are restoring under pressure at 2 AM.
How do you monitor backup jobs and harden the backup path?
A backup job that fails silently for three weeks is worse than no backup at all—you believe you are protected.
Monitoring options
- Cron mail — set
MAILTO=ops@yourdomain.comin root crontab - Exit-code wrappers — append
|| curl -X POST https://hooks.example/alertto scripts - File age checks — Nagios or a simple script alerts if newest dump is older than 26 hours
- Log rotation — keep 30 days of backup logs under
/var/backups/logs
Integrate with your wider Ubuntu server monitoring stack. Backup freshness belongs on the same dashboard as disk space and PHP-FPM status.
Lock down the backup path itself. Restrict SSH keys, disable password auth, and keep the backup user unable to write production code. Ransomware often targets connected backup directories first.
Follow server hardening practices and the broader website and server security guide. Backups do not replace patching, firewall rules, or TLS with Let's Encrypt.
External references worth bookmarking: the MySQL 9.7 mysqldump documentation, the Ubuntu Server documentation, and rclone official docs for remote configuration flags.
If you want hands-off maintenance after setup, support and maintenance services and Linux system administration cover cron audits, restore drills, and alert tuning. Hosting setup should include backup scope in the SLA from day one.
Generate strong credentials for backup accounts with the password generator. Rotate them when staff changes.
Key Takeaways
- Automated server backups complete setup needs database dumps, file sync, off-site copies, and retention—not just one mysqldump line in cron.
- Stagger mysqldump, rsync, and rclone jobs to avoid I/O overlap on small VPS plans common in Nepal.
- Layer Spatie Laravel Backup or WP-CLI exports on top of server-level scripts for application-specific bundles.
- Push encrypted archives to S3 or a remote VPS nightly; local-only copies fail when the server dies.
- Run a monthly restore drill and log results—untested backups are not backups.
- Monitor job exit codes and dump file age; alert before you discover data loss during an incident.
People Also Ask
How often should server backups run?
Daily full database dumps suit most Laravel and WordPress sites. High-traffic eCommerce may need hourly binlog shipping or more frequent incremental sync. Match frequency to your recovery point objective—the maximum data loss you can accept.
What is the best backup retention policy?
A practical default is 14 daily, 8 weekly, and 12 monthly copies. Adjust for compliance and storage cost. Lifecycle rules on S3 buckets automate expiry so old objects do not inflate your bill.
Should backups include vendor and node_modules directories?
No. Exclude vendor/, node_modules/, and cache paths. Rebuild them with composer install and npm ci after restore. Including them wastes space and slows every sync.
Can I rely on hosting panel backups alone?
Panel snapshots help, but they are often local to the provider, opaque, and hard to test. Keep your own scripted dumps you control, verify, and can download. Treat panel backups as a secondary layer.
Build a backup stack you can trust under pressure
An automated server backups complete setup is finished only when a restore succeeds on a clean server—not when cron runs without errors. Script the dumps, sync off-site, monitor freshness, and drill monthly. Pair this with Ubuntu security practices and CIS hardening benchmarks so the data you protect stays reachable.
Need help wiring this on a production stack or auditing an existing cron setup? Contact us for backup review, restore testing, and ongoing server maintenance. Browse the portfolio for examples of live systems running these patterns, or read more on the blog.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

