Set Up Linux Server Backups to S3 With a 7 Day Keep Rule
A dead drive can wipe a site in seconds. Here is how to send copies of your files and your DB to a cloud bucket, and how to keep only the copies you need.
A dead drive can wipe a site in seconds. So can a bad script. A new release can break the code that serves your pages. If you have no copy somewhere else, the work is gone.
A copy on the same disk is not a backup. When the disk fails, the copy fails with it. Search tools drop dead sites fast. AI bots skip broken links. They cite the sites that still answer.
You can stop this. You do not need a paid tool for it. You need a cloud bucket. You need a backup tool. You need a timer that runs each day. This guide shows each step. It ends with a restore test. A copy you never brought back is only a hope.
The 3-2-1-1-0 rule.
Most guides tell you to keep three copies of your data. Two on separate media. One off site. The newer 3-2-1-1-0 rule adds two more rules. It asks for one safe copy. It asks for zero errors when you restore.
Here is what that means for a busy site.
- Three copies of your data. Keep one live copy on disk. Keep one local snap for fast rollbacks. Keep a third copy in the cloud.
- Two media types. Keep live files on fast local disks. Keep cloud snaps in cloud buckets.
- One copy in the cloud. Send snaps to a second data center. Keep that copy outside your own host box.
- One safe copy. Lock cloud data with split keys. A bad script must not wipe past files.
- Zero restore errors. Test your restore each month. A cold copy is not a safe copy.
Follow this flow and your data stays safe. Drive faults cannot touch cloud copies.
Why systemd timers replace cron for backups.
In short: timers give you boot delays. They give you journal logs. They catch up on missed runs. They also give you strong sandbox rules.
Old guides tell you this. Put a shell script in cron. Cron works for small tasks. But cron has blind spots. They hurt live web hosts.
1. One log stream and status checks.
Cron sends output to local mail. When a cron job fails, no one knows. You find out when a crash hits.
Timers run as system units. Each log line goes to the journal. You can check job status with one clean step.
systemctl status vps-backup.service
2. Catching missed runs after node reboots.
Your server reboots during a set cron hour. Cron skips that job. The job does not run until the next day.
Timers fix this with the boot-run rule. If the node was down at night, systemd runs the job. It runs right after boot.
3. Safe state and disk sandbox rules.
Cron runs jobs with full root power. If a script gets lost, the thief gets full control.
Systemd lets you lock down the worker. You can set strict read rules. The worker can read web files. But it cannot alter system tools.
| Feature. | Classic Cron. | systemd timers. | Real World Gain. |
|---|---|---|---|
| run log. | Mail file. | Systemd journal. | Fast search. |
| Missed runs. | Job is skipped. | Runs on boot. | No missed runs. |
| Time jitter. | Fixed minute. | Random delay. | No traffic spikes. |
| Sandbox. | Runs as raw user. | Native Linux cgroups. | Root disk stays safe. |
| Network check. | None. | Waits for network. | Never runs off-line. |
For a live node, timers give clear gains.
Setting up Restic and S3 cloud disks.
In short: get restic. Set up a tight key file with 600 rights. Then start the S3 repo with client keys.
Restic is a modern backup tool. It locks each file with AES-256. The data then leaves your server. It uses chunk sharing. A large file only changes by a few chunks. Restic sends only the new chunks.
Get restic on your node.
Restic is in standard repos on Ubuntu and Debian. Run this step to get the tool.
apt-get update && apt-get install -y restic
Run this step to check the tool.
restic version
Set up S3 access keys.
Create a safe path for backup keys. Keep rights tight so other guests can't read it.
mkdir -p /etc/restic
chmod 700 /etc/restic
Create the key file. Put your S3 endpoint and bucket details in it.
# S3 repository settings
export RESTIC_REPOSITORY="s3:https://s3.in-west-1.webhost24.in/my-server-backups"
export AWS_ACCESS_KEY_ID="YOUR_S3_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="YOUR_S3_SECRET_KEY"
# Encryption password for Restic repository
export RESTIC_PASSWORD="USE_A_STRONG_RANDOM_PASSPHRASE_HERE"
# Local backup directory cache
export RESTIC_CACHE_DIR="/var/cache/restic"
Run this step to lock down file rights.
chmod 600 /etc/restic/restic.env
Start the cloud repo.
Load your keys and run the init step.
set -a
. /etc/restic/restic.env
set +a
restic init
Restic builds the path tree on your S3 bucket. Store your key in a safe password tool. Lose that key and no one can read your snaps.
Live DB and file dump scripts.
Short note: Stream hot DB dumps straight into a safe snap. The site stays live.
Copying a live DB needs care. Copy raw DB files while the engine runs. You get a bad dump. Lock rows and guests see site timeouts.
Hot lock-free MySQL and MariaDB dumps.
Use one view mode with mysqldump. This uses InnoDB snapshot views. The DB keeps serving reads and writes. This goes on while the dump runs.
mysqldump --one-view --quick --routines --triggers --all-DBs | gzip > /var/backups/mysql-all.sql.gz
For PostgreSQL, run pg_dumpall with clean flags.
pg_dumpall --clean | gzip > /var/backups/postgres-all.sql.gz
The live job script.
Create the script in your local bin path.
#!/usr/bin/env bash
set -euo pipefail
# Load credentials
set -a
. /etc/restic/restic.env
set +a
# Setup temp paths
DUMP_DIR="/var/backups/db-dumps"
mkdir -p "$DUMP_DIR"
chmod 700 "$DUMP_DIR"
echo "[$(date -u +%T)] Starting DB dump..."
# Dump MariaDB / MySQL with no row locks
if command -v mysqldump >/dev/null 2>&1; then
mysqldump --one-view --quick --routines --triggers --all-DBs | gzip -c > "${DUMP_DIR}/mariadb-all.sql.gz"
fi
echo "[$(date -u +%T)] Sending the new backup to the cloud..."
# Back up web files, configs, and the DB dumps
restic backup --tag "daily-production" --exclude="/var/www/*/cache/*" --exclude="/var/www/*/tmp/*" /var/www /etc/nginx /etc/ssl /etc/systemd/system "$DUMP_DIR"
# Clean local dump files
rm -rf "$DUMP_DIR"
echo "[$(date -u +%T)] Running the 7-day keep rule..."
# Keep daily backups for 7 days and prune old blocks
restic forget --keep-daily 7 --prune
echo "[$(date -u +%T)] Backup and 7-day keep rule complete."
Make the script ready to run with chmod.
chmod 700 /usr/local/bin/vps-backup-s3.sh
This script dumps DB rows hot. It uploads new chunks to S3. Then it runs a strict 7-day keep rule.
Running the 7-day keep rule.
In short: Run restic forget with 7 daily keeps. Prune to clean old chunks.
Old backups eat up disk space. With no clean up rule, your S3 bucket grows. It grows with no cap. That raises monthly bills.
A 7-day keep rule keeps your data safe. It also controls costs. Make a bad edit on Tuesday. You can restore from Monday, Sunday. Or any of the past 7 days.
How the 7-day keep rule cleans up.
The forget step marks old snaps for drop. The prune flag cleans stale chunks from S3.
restic forget --keep-daily 7 --prune
7-Day keep rule Timeline (Rolling Window)
┌────────┬────────┬────────┬────────┬────────┬────────┬────────┐
│ Day -7 │ Day -6 │ Day -5 │ Day -4 │ Day -3 │ Day -2 │ Day -1 │
│ Kept │ Kept │ Kept │ Kept │ Kept │ Kept │ Kept │
└────────┴────────┴────────┴────────┴────────┴────────┴────────┘
▲
│ Anything older than 7 days is purged by --prune
Restic shares common chunks. A file that does not change in 7 days. It shares the same chunks. Storing 7 daily snaps uses far less disk space. It beats 7 raw copies.
| Day in keep rule. | Web Files. | DB Dump. | Chunks Stored. | Disk Space Used. |
|---|---|---|---|---|
| Day 1 (Base). | 20 GB. | 1 GB. | Full base chunks. | 21 GB. |
| Day 2. | 20 GB. | 1 GB. | New chunks. | +250 MB. |
| Day 3. | 20 GB. | 1 GB. | New chunks. | +280 MB. |
| Day 4. | 20 GB. | 1 GB. | New chunks. | +240 MB. |
| Day 5. | 20 GB. | 1 GB. | New chunks. | +310 MB. |
| Day 6. | 20 GB. | 1 GB. | New chunks. | +290 MB. |
| Day 7. | 20 GB. | 1 GB. | New chunks. | +300 MB. |
| Total Stored. | 7 Days. | 7 Restores. | Deduplicated. | ~22.8 GB total. |
Without chunk sharing, 7 full copies would take 147 GB. With Restic on S3, you get 7 daily restore points. It costs less than 23 GB.
Automate it with systemd job and timer units.
Short note: Create a systemd job unit. It runs the script. Then pair it with an OnCalendar timer unit.
We need two small files in the systemd path.
1. The systemd job unit.
Create the job unit file on disk.
[Unit]
Description=Daily backup to the cloud bucket
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/vps-backup-s3.sh
User=root
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7
# Sandbox the job
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
The low task priority rules keep the job from slowing down. They protect active web guests. Your site stays fast while the job runs.
2. The systemd timer unit.
Create the timer unit file on disk.
[Unit]
Description=Run the daily backup at 3:00 AM IST
Requires=vps-backup.service
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
Here is what these rules do.
- The time rule. Runs each day at 3:00 AM. This is most often the lowest traffic hour in India.
- Random delay. Adds up to 30 min of random delay. This spreads out network load.
- The catch-up flag. Runs the job right away. The node was off at 3:00 AM.
Turn on and test the timer.
Run this step to reload systemd. It starts the timer.
systemctl daemon-reload
systemctl enable --now vps-backup.timer
Check that the timer is active with list timers.
systemctl list-timers --all | grep vps-backup
You can trigger a manual test run at any time.
systemctl start vps-backup.service
Watch live logs to check success with journalctl.
journalctl -u vps-backup.service -f
Restore drills in under five min.
Short note: A copy you have never run. It is just a cold wish. Test your restore in a sandbox.
Never wait for a crash to learn how to restore. Run a test restore once each month.
1. View your found snaps.
Load your keys and list all snaps with restic.
set -a
. /etc/restic/restic.env
set +a
restic snapshots
Restic prints an output list on screen.
ID Time Host Tags Paths
──────────────────────────────────────────────────────────────────────────────────
a3f81c01 2026-10-24 03:12:00 server-1 daily-production /var/www, /etc/nginx...
b7e92d04 2026-10-25 03:15:00 server-1 daily-production /var/www, /etc/nginx...
──────────────────────────────────────────────────────────────────────────────────
2 snapshots
2. Test file restore.
Restore files to a temp path to check chunks.
mkdir -p /tmp/restore-test
restic restore latest --target /tmp/restore-test --path "/var/www"
Check your files in the test path.
ls -la /tmp/restore-test/var/www
rm -rf /tmp/restore-test
3. Test DB restore.
Extract the latest DB dump with restic.
mkdir -p /tmp/db-test
restic restore latest --target /tmp/db-test --path "/var/backups/db-dumps"
Restore it into a test DB to check rows.
mysql -u root -p -e "CREATE DB IF NOT EXISTS restore_test;"
gunzip < /tmp/db-test/var/backups/db-dumps/mariadb-all.sql.gz | mysql -u root -p restore_test
mysql -u root -p -e "SHOW TABLES IN restore_test;"
mysql -u root -p -e "DROP DB restore_test;"
rm -rf /tmp/db-test
These steps finish with no errors. Your restore plan works.
Local data rules and DPDPA 2023 in India.
Where your backups live matters. It matters as much as how they run.
Serve guests in India. Your DB holds Indian guest data. The Digital Personal Data Protection Act (DPDPA 2023) covers this. Firms must protect that data. They must do this with care.
Why Indian cloud disks matter.
- Local data rules. Store copies on Indian nodes. Guest data stays inside local borders. You avoid complex foreign transfer rules.
- Lower network ping. Send snaps to an Indian S3 region. You then use fast local paths. Transfers finish in min instead of hours.
- Zero foreign exchange fees. Store copies on local nodes. You avoid price rate shifts. You pay in Indian Rupees (INR).
- No foreign egress costs. Move data inside India. Bandwidth bills go down.
You can learn more about these legal rules. Read our guide on data sovereignty and DPDPA 2023.
Auto health checks and failure warns.
To sleep well at night, you need a warn. It must fire when a job fails.
Create a failure warn job.
Create the warn job unit file.
[Unit]
Description=Backup failure alert
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-alert.sh %i
User=root
Create the warn shell script.
#!/usr/bin/env bash
UNIT_NAME="$1"
MESSAGE="ALERT: Backup failed on $(hostname) for unit ${UNIT_NAME} at $(date -u +%T)"
logger -t "vps-backup" "$MESSAGE"
# Log the failure to a local file
echo "$MESSAGE" >> /var/log/backup-failures.log
Give the script run rights with chmod.
chmod 700 /usr/local/bin/backup-alert.sh
Link the warn job to your backup unit.
Open the backup job. Add the failure rule under the unit block.
[Unit]
Description=Daily backup to the cloud bucket
After=network-online.target
Wants=network-online.target
OnFailure=vps-backup-notify@%n.service
Run this step to reload systemd units.
systemctl daemon-reload
Now, if a job fails, systemd acts fast. It runs the warn script right away.
Check repo health.
Once each week, run a quick check. It makes sure S3 objects have no bit rot.
restic check --read-blocks-subset=5%
This reads a random 5% slice of your repo chunks. It checks hash sums.
Summary checklist.
Here is the quick review list for your Linux server backup plan.
- Get restic and create the key file with 600 rights.
- Start the cloud S3 repo with restic init.
- Deploy the script with hot DB dumps.
- Set a 7-day keep rule with prune.
- Create the systemd job and timer units with catch-up rules.
- Turn on the timer with systemctl enable.
- Run a test restore into a temp path to prove restore works.
- Make sure cloud data rests in India for DPDPA rules.
Need fast managed app hosting with low ping across India? Or our hosting plans? WebHost24 gives high-speed nodes. Each plan comes with month-to-month billing. It also has a 7-day keep rule. You also get a 30-day money-back promise.
Questions about setting up backups or node move? Message our tech team on WhatsApp for direct help.