Backups are always the last line of defense, so I set a clear principle for my Homelab: keep at least three copies of the same data, and one of them must be off-site. This project was the research done for that purpose—I went through every free cloud storage service available on the market one by one, and finally put together a plan that can run long term.
Conclusion: none of the free tiers from any provider can hold my 300GB of photos. Free tiers generally offer only 10–20GB, enough only for a few GB of items such as configs, databases, and important documents; for the full photo set, I either pay or fall back to an external drive for an offline copy.
So the plan is split into two layers: small critical data goes to the Backblaze B2 free tier, plus the 20GB of unused allowance on a Hong Kong VPS—both copies cost nothing; the full photo set is temporarily stored offline on an external drive, and if I'm ever willing to pay, I'll consider paid B2 (about $6.95/TB/month).
Automation is handled by restic: photos run every Sunday at 03:00, configs and databases run every day at 03:00, the scripts use flock -n to prevent duplicate execution, and once a month restic check --read-data-subset=5% is used for a spot check.
This project is now archived: the conclusion is settled and the scripts are running; what remains is to come back and adjust the parameters when the photos grow to the point where paying becomes necessary.
Features
- 逐一对比免费云盘的额度与限制,明确“免费装不下 300GB 照片”的结论
- 把数据拆成小体积关键数据与照片全量两层,分别匹配方案
- 主力副本:restic + Backblaze B2(S3 兼容,先跑 10GB 免费层)
- 第二副本:香港 VPS 已有的 20GB 空余额度,无需额外注册
- 配置、数据库每天备份,照片每周备份,cron 定时 + flock 防止重复执行
- 每月做一次 read-data-subset 抽查,留出恢复演练的流程步骤
Stack: restic · rclone · Backblaze B2 · Cloudflare R2 · S3 · NAS · cron · flock
1. Create a bucket
Sign up for Backblaze B2, create a new private bucket, and note down the keyID, applicationKey, as well as the S3 endpoint and region shown in the bucket details. Don't top up yet—the 10GB free tier is enough to get the whole flow working.
2. Initialize the restic repository
export RESTIC_REPOSITORY="s3:s3.<region>.backblazeb2.com/<bucket>"
export AWS_ACCESS_KEY_ID="<keyID>"
export AWS_SECRET_ACCESS_KEY="<applicationKey>"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
restic init
Store the password in a separate file with permissions set to 600—don't commit it to git alongside the scripts.
3. Write the backup script
#!/usr/bin/env bash
set -euo pipefail
export RESTIC_REPOSITORY="s3:s3.<region>.backblazeb2.com/<bucket>"
export AWS_ACCESS_KEY_ID="<keyID>"
export AWS_SECRET_ACCESS_KEY="<applicationKey>"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
restic backup /srv/data/config /srv/data/db --tag daily
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
For photos, use the same script—just change the paths and use --tag photos. When executing, always wrap it in flock -n /var/lock/restic.lock <script> to prevent the next run from starting before the previous one finishes.
4. Set up scheduling
Add two lines with crontab -e:
0 3 * * * flock -n /var/lock/restic-daily.lock /opt/backup/daily.sh
0 3 * * 0 flock -n /var/lock/restic-photo.lock /opt/backup/photos.sh
Sync the second copy to a spare 20GB directory on the Hong Kong VPS—you can use restic's multiple repositories directly, or rclone sync.
5. Spot checks and restore drills
restic snapshots
restic check --read-data-subset=5%
restic restore latest --target /tmp/restore-test
Run this once a month to confirm files can actually be restored; backing up without verifying is the same as having no backup at all.
6. Things you need to test yourself
The upload/download speeds of B2 / R2 from your location, and the specific terms of the free tier—these vary between providers, so double-check the official pricing page before finalizing your plan.