一台独立开发者常用的 1C2G 或 2C4G VPS,最怕的不是机器坏,而是你以为自己有备份。云厂商备份、Restic、Uptime Kuma 各管一段:云备份管整机回滚,Restic 管单文件和配置找回,Uptime Kuma 管故障发现,恢复演练管这套东西到底能不能跑起来。
这篇按 2026 年 5 月 24 日比对的 Restic、Uptime Kuma、Hetzner Cloud 和 DigitalOcean 文档来写。没有假设你有 SRE 团队,默认就是一个人维护 SaaS、博客、API、Stripe webhook 和几个 Docker 服务。
最低配置到底是哪几项?
把目标压低:不是做银行级容灾,而是让一台 VPS 在误删文件、升级失败、磁盘坏掉、机房故障时有路可退。最低配置是 4 件事。
| 层 | 最低配置 | 频率 | 解决什么问题 |
|---|---|---|---|
| 文件级备份 | Restic 备份 /etc、应用目录、数据库导出 | 每日 1 次 | 找回配置、上传文件、单表导出 |
| 整机镜像 | 云厂商自动备份或手动快照 | 每日或发布前 | 系统级回滚、快速复制机器 |
| 可用性监控 | Uptime Kuma 外部探测 HTTPS / TCP / 证书 | 1 分钟 | 比用户更早知道挂了 |
| 恢复演练 | 新建临时 VPS 还原一次 | 每季度 1 次 | 验证密码、命令、依赖和文档 |
Restic 官方文档把一次目录备份称为 snapshot,restic backup 会保存目录在某一刻的内容,并用快照 ID 标识。云厂商的 snapshot 也是某一刻磁盘镜像,但它通常不能优雅处理运行中的数据库写入,也不方便只恢复一个文件。
Restic 怎么配才算能用?
先别追求复杂仓库。个人项目用一个远端仓库,按主机名和路径分组,能恢复就行。你至少要备份三类目录:系统配置、应用目录、数据库导出目录。
export RESTIC_REPOSITORY="sftp:backup@example:/srv/restic/saas-prod"
export RESTIC_PASSWORD_FILE="/root/.config/restic/pass"
restic init
mkdir -p /var/backups/app
sqlite3 /srv/app/data/app.db ".backup '/var/backups/app/app.db.sqlite'"
restic backup /etc /srv/app /var/backups/app \
--exclude /srv/app/node_modules \
--exclude /srv/app/.next/cache \
--exclude /srv/app/logs
如果是 Postgres 或 MySQL,不要直接把正在写入的数据目录丢给 Restic,用 pg_dump、mysqldump、物理备份工具或应用自带导出生成一致文件,再让 Restic 备份导出结果。
保留策略不要太贪心。一个轻量 SaaS 可以先用这条:
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check
Restic 文档说明,删除快照需要 forget,真正清理不再引用的数据需要 prune;forget --prune 可以合在一起做。prune 会锁仓库,安排在业务低峰,不要和备份任务撞在同一分钟。
Uptime Kuma 放在哪里?
Uptime Kuma 不该和被监控业务放在同一台 VPS。官方安装页在 2026 年 4 月更新后给出的 Docker 示例使用 louislam/uptime-kuma:2,并把数据挂到 /app/data。那页还提醒 SQLite 需要 POSIX 文件锁,/app/data 要映射到本地目录或本地卷,别放到不可靠的网络文件系统上。
docker run -d \
--restart=unless-stopped \
-p 3001:3001 \
-v uptime-kuma:/app/data \
--name uptime-kuma \
louislam/uptime-kuma:2
最低监控项我会这样建:
| 监控项 | 类型 | 间隔 | 告警条件 |
|---|---|---|---|
| 首页或健康检查 | HTTP(s) | 60 秒 | 非 2xx/3xx 或超时 |
| API 端口 | TCP Port | 60 秒 | 端口不通 |
| 证书 | HTTPS 证书 | 每日 | 剩余天数过低 |
| 关键 webhook | HTTP(s) | 60 秒 | Stripe / GitHub 回调入口失败 |
| 备份心跳 | Push Monitor | 每日 | Restic 任务没汇报成功 |
最容易漏的是备份心跳。备份脚本最后一行 curl 一下 Uptime Kuma 的 push URL;如果 cron 没跑、密码文件丢了、远端仓库挂了,你会在第二天发现,而不是一个月后恢复时才发现。
云快照和自动备份怎么选?
云厂商备份是兜底,不是唯一备份。Hetzner 文档写得很清楚:Backups 和 Snapshots 都是服务器磁盘副本,可以用于 rebuild 原服务器或创建新服务器;Backups 自动每日创建,每台服务器最多 7 个槽位,满了会删最旧的;Snapshots 手动创建,保留到你自己删除。
DigitalOcean 的 Droplet backups 是自动创建的磁盘镜像,文档列出 4 小时、6 小时、12 小时、每日、每周等间隔;它也提醒备份不包含 volumes。DigitalOcean snapshots 则是按需磁盘镜像,可以创建新 Droplet,也可以把现有 Droplet 恢复到某个时间点,但恢复会替换原 Droplet 上该时间点之后的数据。
| 场景 | 用什么 | 操作习惯 |
|---|---|---|
| 每天防整机事故 | 自动备份 | 打开云厂商 Backups,记下保留周期 |
| 发布大版本前 | 手动快照 | 发布前创建,验证后保留 7-14 天 |
| 误删单个文件 | Restic | restic ls latest 找路径后恢复 |
| 数据库逻辑错误 | 数据库导出 + Restic | 找最近一次一致导出,不直接回滚整机 |
| 迁移到新区域 | 快照创建新机器 | DNS 切换前先跑健康检查 |
快照的坑在 volumes。Hetzner 明确说 Backups 和 Snapshots 不包含挂载到 server 的 Volumes;DigitalOcean 也说明 Droplet automated backups 不包含 volumes,要单独给 volume 做 snapshot。你的上传文件、Postgres 数据、MinIO 数据如果放在独立 volume 上,必须单列一条备份任务。
恢复演练按什么顺序做?
恢复演练不要在生产机上赌。新建一台临时 VPS,先不切 DNS,跑完之后删掉。目标不是漂亮截图,而是拿到三个数字:恢复耗时、缺失项、人工步骤。
| 步骤 | 动作 | 通过标准 |
|---|---|---|
| 1 | 新建临时 VPS,装 Docker、Caddy 或 Nginx | 机器能 SSH,基础端口打开 |
| 2 | 安装 Restic,配置仓库地址和密码文件 | restic snapshots 能列出生产快照 |
| 3 | 恢复到临时目录 | restic restore latest --target /tmp/restore 成功 |
| 4 | 拷贝配置和应用文件 | .env、compose 文件、反代配置齐全 |
| 5 | 导入数据库导出 | 应用能读到最近一份数据 |
| 6 | 临时域名或 hosts 访问 | 首页、登录、支付回调健康检查通过 |
| 7 | 记录结果 | 写下 RTO、缺文件、下次要自动化的命令 |
Restic 支持用 latest 恢复最近快照,也支持按 --host、--path 过滤。别一上来原地覆盖生产目录;Restic 文档提醒,原地恢复如果中断,目标文件可能处于部分恢复状态。先恢复到 /tmp/restore,确认内容后再复制。
哪些东西不要备份?
不要把缓存、构建产物和依赖目录塞进备份仓库。node_modules、.next/cache、Docker layer、临时上传缓存、日志压缩包都可能让备份变慢,还会让恢复时看不清真正重要的数据。
真正要保住的是这些:
/etc/nginx、/etc/caddy、systemd unit、cron 文件docker-compose.yml、.env.example、实际环境变量的安全副本- SQLite 文件的一致副本,或 Postgres / MySQL dump
- 用户上传目录、发票 PDF、授权文件、手写脚本
- DNS、支付 webhook、OAuth callback 这类后台路径的截图或文本记录
李辰在小李出海笔记写工具栈文章时,默认读者是一个人维护产品。这个规模下,最贵的不是多买一份备份,而是半夜恢复时才发现 .env 只在旧服务器上,备份仓库密码也只存在旧服务器上。
这套方案不覆盖什么?
这不是多区域高可用方案。它不能保证数据库零丢失,不能替代应用层审计日志,也不能解决你误把坏数据同步进所有备份的问题。
它也没有覆盖 Kubernetes、对象存储版本控制、Postgres PITR、主从复制和跨云热备。等你的项目有付费团队、合规要求或明确 RPO/RTO 指标,再把这些加进来。个人 SaaS 的第一步,是把「能从昨天恢复」落实。
相关阅读
- GitHub Actions Cloudflare 超时排查 — CI/CD平台的网络问题
- Docker npm PyPI 访问慢 SOP — 开发环境的网络问题排查
- Vercel build out of memory — 构建环境的常见问题
实际演练中容易忽略的坑
备份仓库密码怎么传递?
密码不要只放在 VPS 本机。放到密码管理器或团队密钥库,并保留一份离线应急记录。团队协作时用共享 vault 而不是聊天窗口发密码。
备份心跳怎么接进日常?
备份脚本最后一行 curl Uptime Kuma 的 push URL;如果 cron 没跑、密码文件丢了、远端仓库挂了,第二天就能发现,而不是下个月恢复时才察觉。
SQLite、Postgres、MySQL 的备份先后顺序
SQLite 小站先做 .backup 再让 Restic 收走;Postgres 用 pg_dump,MySQL 用 mysqldump。核心原则都一样:先生成一致副本,再让 Restic 备份导出结果,绝不直接备份运行中的数据文件。