On this page
Introduction
Running a $5 VPS is a great way to get a Linux foothold without breaking the bank, but it also means you have to be extra careful with data durability. A single disk failure or accidental rm -rf can wipe out weeks of work. In this guide I’ll walk you through a real‑world, production‑ready backup pipeline:
- Install and configure Restic – a fast, deduplicating, encrypted backup tool.
- Deploy MinIO as a self‑hosted S3‑compatible object store on the same VPS (or a second cheap droplet).
- Wire Restic to push incremental backups to MinIO.
- Automate the whole thing with systemd timers.
- Verify the restore process end‑to‑end.
Everything is done with free, open‑source software, runs on a 1 CPU / 1 GB RAM VPS, and costs virtually nothing beyond the VPS fee.
Why MinIO?
It gives you an S3‑compatible API you can point any tool at, without paying for a third‑party bucket. You keep the data in your control, and you can later migrate to any real S3 provider if you need more space.
Prerequisites
| Item | Minimum requirement | Why it matters |
|---|---|---|
| VPS | 1 vCPU, 1 GB RAM, 25 GB SSD (any $5 plan) | Restic is lightweight; MinIO needs ~200 MB RAM for small workloads |
| OS | Ubuntu 22.04 LTS (or Debian 12) | Official packages and documentation |
| Root or sudo access | — | Needed to install packages and set up system services |
| Domain (optional) | backup.example.com pointing to VPS |
Makes TLS setup with Let’s Encrypt easier |
If you already have a VPS, you can skip the provisioning steps and jump straight to the installation sections.
1. Install MinIO
1.1 Download the binary
# Create a dedicated user
sudo useradd -r -s /usr/sbin/nologin minio
sudo mkdir -p /opt/minio/{data,config}
sudo chown -R minio:minio /opt/minio
# Grab the latest stable release (as of writing)
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
1.2 Create a systemd service
Create /etc/systemd/system/minio.service:
[Unit]
Description=MinIO Object Storage
After=network.target
[Service]
User=minio
Group=minio
ExecStart=/usr/local/bin/minio server /opt/minio/data \
--address ":9000" \
--console-address ":9001"
Environment="MINIO_ROOT_USER=adminuser"
Environment="MINIO_ROOT_PASSWORD=StrongP@ssw0rd!"
Restart=always
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Tip: Replace
adminuserandStrongP@ssw0rd!with a strong, unique password. Store them in a password manager – you’ll need them for Restic later.
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now minio
1.3 Verify MinIO
Open http://<VPS_IP>:9000 in a browser, log in with the credentials you set, and you should see the MinIO console. For a headless test, use mc (MinIO client) later.
2. Set Up an S3 Bucket for Restic
We’ll create a bucket called restic-backups.
# Install mc (MinIO client)
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
# Configure alias for our server
mc alias set local http://127.0.0.1:9000 adminuser StrongP@ssw0rd!
# Create bucket
mc mb local/restic-backups
You can also enable versioning (optional but handy):
mc version enable local/restic-backups
3. Install Restic
# Add the official Restic repository (Ubuntu/Debian)
echo "deb http://download.opensuse.org/repositories/home:/stevenroose:/restic/Debian_12/ /" \
| sudo tee /etc/apt/sources.list.d/restic.list
wget -qO - https://download.opensuse.org/repositories/home:/stevenroose:/restic/Debian_12/Release.key \
| sudo apt-key add -
sudo apt update
sudo apt install restic -y
Verify:
restic version
# restic 0.16.4 compiled with go1.22.2 on linux/amd64
4. Initialise the Restic Repository
Restic stores its metadata inside the bucket. Initialise it once:
export RESTIC_REPOSITORY=s3:http://127.0.0.1:9000/restic-backups
export RESTIC_PASSWORD=AnotherStrongPassphrase!
restic init
You should see something like:
create repository s3:http://127.0.0.1:9000/restic-backups
created new restic repository f3b5... at s3:http://127.0.0.1:9000/restic-backups
Important: Keep RESTIC_PASSWORD safe. Without it you cannot read the backups.
5. Create a Backup Script
We’ll back up three typical directories:
/etc– system configuration/var/www– web app files (if you host a site)/home– user data
Create /usr/local/bin/restic-backup.sh:
#!/bin/bash
set -euo pipefail
# ---------- Environment ----------
export RESTIC_REPOSITORY=s3:http://127.0.0.1:9000/restic-backups
export RESTIC_PASSWORD=AnotherStrongPassphrase!
export AWS_ACCESS_KEY_ID=adminuser
export AWS_SECRET_ACCESS_KEY=StrongP@ssw0rd!
# ---------- Logging ----------
LOGFILE="/var/log/restic-backup.log"
exec > >(tee -a "$LOGFILE") 2>&1
echo "=== Restic backup started $(date -u) ==="
# ---------- Backup ----------
restic backup /etc /var/www /home \
--exclude="/home/*/.cache" \
--exclude="/var/www/tmp" \
--tag "$(hostname)" \
--verbose
# ---------- Prune old snapshots ----------
# Keep last 7 daily, 4 weekly, 6 monthly
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
echo "=== Restic backup finished $(date -u) ==="
Make it executable:
sudo chmod +x /usr/local/bin/restic-backup.sh
sudo chown root:root /usr/local/bin/restic-backup.sh
5.1 Why the forget command?
Restic stores incremental snapshots; each backup only adds new or changed chunks. Over time you’ll accumulate many snapshots. restic forget combined with --prune removes old snapshots and frees space in the bucket while keeping a sensible retention policy.
6. Automate with systemd Timer
6.1 Service unit
Create /etc/systemd/system/restic-backup.service:
[Unit]
Description=Restic incremental backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-backup.sh
6.2 Timer unit
Create /etc/systemd/system/restic-backup.timer:
[Unit]
Description=Run Restic backup daily at 02:30
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=15m # prevents thundering herd if many VPS start together
[Install]
WantedBy=timers.target
Enable and start the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
You can verify the next run:
systemctl list-timers --all | grep restic-backup
6.3 Testing the timer
Trigger a manual run to ensure everything works:
sudo systemctl start restic-backup.service
Check the log:
tail -n 20 /var/log/restic-backup.log
You should see a summary like:
snapshot 1a2b3c4d saved
files: 3,123, total size: 4.5 GiB
...
7. Restic Restore Procedure
Never assume a backup works until you’ve restored it once. Here’s a quick checklist:
7.1 List available snapshots
restic snapshots
You’ll get a table with IDs, dates, and tags.
7.2 Restore a specific snapshot
# Example: restore the most recent snapshot to /tmp/restore-test
SNAP=$(restic snapshots --latest 1 --json | jq -r '.[0].short_id')
restic restore $SNAP --target /tmp/restore-test
7.3 Verify critical files
diff -r /etc /tmp/restore-test/etc | less
If the diff is empty, your backup is good. Delete the test directory afterwards:
rm -rf /tmp/restore-test
7.4 Full disaster recovery
If the VPS disk is completely lost, you can spin up a fresh VPS, install MinIO and Restic, then pull the repository:
# On the new machine
sudo apt install restic mc -y
export RESTIC_REPOSITORY=s3:http://<OLD_VPS_IP>:9000/restic-backups
export RESTIC_PASSWORD=AnotherStrongPassphrase!
export AWS_ACCESS_KEY_ID=adminuser
export AWS_SECRET_ACCESS_KEY=StrongP@ssw0rd!
# Verify we can list snapshots
restic snapshots
# Restore everything to root
restic restore latest --target /
Caution: Restoring to
/will overwrite existing files. In a brand‑new VM this is fine; otherwise restore to a temporary location and copy selectively.
8. Monitoring & Alerts (Optional)
For a $5 VPS you probably don’t have a full monitoring stack, but a simple email alert on failure costs almost nothing.
Create /etc/systemd/system/[email protected]:
[Unit]
Description=Send email on Restic backup failure
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-failure-notify.sh %i
And the script /usr/local/bin/restic-failure-notify.sh:
#!/bin/bash
set -euo pipefail
RECIPIENT="[email protected]"
SUBJECT="Restic backup failed on $(hostname)"
BODY="/var/log/restic-backup.log"
if grep -q "error" "$BODY"; then
echo -e "Subject: $SUBJECT\n\n$(tail -n 20 $BODY)" | sendmail -t "$RECIPIENT"
fi
Install sendmail (or mailutils) and enable the failure service via a OnFailure= directive in the main service unit:
# In restic-backup.service, add:
OnFailure=restic-backup-failure@%p.service
Now any non‑zero exit from the backup will trigger an email.
9. Comparison: Restic vs. Other Backup Tools on a $5 VPS
| Feature | Restic | BorgBackup | Duplicati |
|---|---|---|---|
| Encryption | Built‑in AES‑256 (client‑side) | Built‑in (AES‑256) | AES‑256 (via .NET) |
| Deduplication | Chunk‑level, works across snapshots | Chunk‑level, similar | File‑level only |
| S3 Compatibility | Native s3: backend |
Requires borgmatic + rclone |
Direct S3 support |
| Resource Usage | ~30 MB RAM, low CPU | ~50 MB RAM, moderate CPU | ~100 MB RAM, Java/.NET overhead |
| Ease of Automation | systemd timers + restic forget |
similar, but extra scripts for prune | Web UI needed for schedule |
| Community & Docs | Very active, simple CLI | Active, but more complex CLI | Smaller, UI‑centric |
For a tiny VPS, Restic wins on memory footprint and native S3 support, making it the most pragmatic choice.
10. Checklist – Are You Ready for Production?
- MinIO installed, running, and reachable on port 9000 (or behind a reverse proxy).
- Bucket
restic-backupscreated with versioning (optional). - Restic repository initialized with a strong password.
- Backup script (
restic-backup.sh) tested manually and logs written to/var/log/restic-backup.log. - systemd service & timer enabled, next run shown by
systemctl list-timers. - Retention policy (
restic forget) matches your storage budget. - Restore test performed on a separate directory and critical files verified.
- Failure alerts (email or Slack) configured (optional).
- Credentials stored securely (consider a
.envfile withchmod 600).
If you tick all the boxes, you have a resilient, off‑site backup pipeline that costs virtually nothing beyond your $5 VPS.
11. Frequently Asked Questions
| Question | Answer |
|---|---|
| Do I need a separate VPS for MinIO? | Not strictly. You can run MinIO and your apps on the same box if you keep the storage volume separate (e.g., /opt/minio-data). For higher durability, spin a second cheap droplet and point Restic to it via its public IP. |
| How much storage does MinIO need? | MinIO itself stores only metadata; the bulk is your backup data. Start with a 10 GB volume; Restic’s deduplication usually reduces a 50 GB source to 5–15 GB in the bucket. |
| Can I encrypt the MinIO bucket itself? | MinIO supports server‑side encryption (SSE‑S3) but Restic already encrypts data before upload. Double‑encrypting adds CPU overhead without much benefit. |
| What happens if the VPS clock drifts? | Restic timestamps snapshots using the system clock. Use systemd-timesyncd or chrony to keep the clock accurate; otherwise snapshot ordering may look odd. |
| Is it safe to expose MinIO to the internet? | Yes, if you enable TLS (via a reverse proxy like Caddy or Nginx) and use strong credentials. Consider firewall rules that only allow your own IPs, or use a VPN. |
12. Wrap‑up
You now have a fully automated, incremental backup solution that fits comfortably on a $5 VPS. By leveraging Restic’s encryption, MinIO’s S3 compatibility, and systemd timers, you get:
- Zero‑cost off‑site storage (the same VPS or a second cheap node)
- Fast, deduplicated backups – only changed chunks travel over the network
- Built‑in retention – keep daily, weekly, and monthly snapshots without manual cleanup
- Simple restore – one command brings back any file or full system
Remember, backups are only as good as the last successful restore you performed. Schedule a quarterly full‑system restore test, rotate your passwords, and keep an eye on MinIO’s disk usage.
If you run into any quirks or want to share your own tweaks, feel free to drop a comment on mahbuburriad.com – we love hearing how the community makes low‑budget backups rock. Happy backing up!