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 平台。