3-2-1 原则
正式说法:3 份副本、2 种介质、1 份异地。
对小项目实操:
- 1 份在服务器本地(
/backups/) - 1 份在对象存储(S3 / R2 / OSS)
- 偶尔下载到本地一份做冷备
备什么
- 数据库(Postgres / Redis 持久化)
- 用户上传文件
- 应用配置 /
.env(加密后) - 不需要备:代码(git 已经是备份)、可重建的容器镜像
用 rclone 同步到对象存储
rclone 是瑞士军刀,支持几十种云存储:
sudo apt install rclone
rclone config # 交互式配置(按提示选 S3 / R2 / OSS / 阿里 / 腾讯)
之后:
rclone copy /backups r2:my-backup-bucket --progress
rclone sync /backups r2:my-backup-bucket --backup-dir r2:my-backup-bucket/old-$(date +%F)
copy 只加新的;sync 会删源里没有的;--backup-dir 把删除/覆盖的存到一个时间戳目录里防误删。
一份完整脚本
/usr/local/bin/backup.sh:
#!/bin/bash
set -e
DATE=$(date +%F)
BACKUP_DIR=/backups
mkdir -p $BACKUP_DIR
# 1. 数据库
docker compose -f /home/wadely/app/docker-compose.yml exec -T db \
pg_dump -U app app | gzip > $BACKUP_DIR/db-$DATE.sql.gz
# 2. 用户上传
tar czf $BACKUP_DIR/uploads-$DATE.tar.gz /home/wadely/app/uploads/
# 3. 推到对象存储
rclone copy $BACKUP_DIR r2:my-backup --include "*-$DATE.*"
# 4. 本地清理 14 天前的
find $BACKUP_DIR -name "*.gz" -mtime +14 -delete
可执行 + cron:
sudo chmod +x /usr/local/bin/backup.sh
echo "0 3 * * * wadely /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1" | sudo tee /etc/cron.d/backup
加密敏感数据
.env 等密钥文件备份前加密:
gpg --symmetric --cipher-algo AES256 .env
# 输入密码,得到 .env.gpg
密码不要也存到对象存储——存到本地的 1Password / Bitwarden。
测试恢复(重要!)
没测过的备份等于没备份。每月做一次:
# 拉一份回来
rclone copy r2:my-backup/db-2026-05-01.sql.gz /tmp/
# 在临时数据库恢复
docker run --rm -v /tmp:/in postgres:16-alpine \
bash -c "gunzip < /in/db-2026-05-01.sql.gz | psql ..."
能恢复成功 + 数据对得上 = 备份有效。
对象存储成本对比
| 平台 | 价格 | 备注 |
|---|---|---|
| Cloudflare R2 | $0.015/GB/月 + 0 出流量费 | 小项目首选 |
| AWS S3 | $0.023/GB/月 + 出流量贵 | 标准选项 |
| Backblaze B2 | $0.005/GB/月 | 最便宜 |
| 阿里 OSS | ~¥0.12/GB/月 | 国内 |
10GB 备份每月 ¥1–10,性价比极高。
下一篇:把站点本身托管到 Pages 平台。