别只会快照:用 Restic + MinIO 给 Homelab 做一套可恢复的文件级备份

在 Homelab 中用 MinIO 搭建 S3 兼容备份仓库,并用 Restic 实现 Linux、Docker 数据目录和 NAS 共享的加密增量备份,覆盖自动化脚本、保留策略、校验恢复与踩坑。

前言:快照不是备份,能恢复才算闭环

很多 Homelab 一开始都会依赖 PVE 快照、ZFS snapshot、NAS 回收站来“防误删”。这些能力很好,但它们本质上更偏向本机时间点回滚,并不天然解决几个问题:

  1. 宿主机或存储池整体损坏时,快照也跟着没了;
  2. Docker 应用的数据散落在 /opt/var/lib/docker/volumes、NFS 挂载点里,PVE VM 级备份粒度太粗;
  3. 想恢复单个配置文件、某个数据库 dump、某张照片时,整机恢复成本太高;
  4. 没有定期校验和恢复演练,备份文件存在不等于可用。

所以这篇文章不讲 PVE 核显 SR-IOV、vGPU,也不重复 PBS 整机备份,而是做一套文件级、加密、增量、可校验、可自动化的 Homelab 备份方案:

  • MinIO:在内网提供 S3 兼容对象存储,作为 Restic 仓库后端;
  • Restic:负责客户端加密、去重、增量备份、保留策略和恢复;
  • systemd timer:替代脆弱的 crontab,做可观测的定时任务;
  • check + 恢复演练:确认备份真的能用。

MinIO 官方文档强调容器部署时磁盘、控制器和网络会直接影响性能;Restic 官方文档也支持用环境变量配置仓库地址和密码,适合无人值守备份脚本。

方案架构

推荐拓扑如下:

角色示例 IP作用建议
MinIO 备份端192.168.10.20S3 兼容对象存储独立磁盘 / NAS / 另一台机器 ✅
Docker 主机192.168.10.30运行应用容器备份 /opt/app、compose、数据库 dump
PVE / Linux 主机192.168.10.10宿主机配置备份 /etc、脚本、关键配置
异地副本可选防火灾/误删/勒索rclone 同步到云端或另一地点 🏆

核心原则:备份端不要和生产数据在同一块盘上。如果 MinIO 和 Docker 服务都跑在同一台机器同一块 SSD 上,那只能防误删,不能防硬盘损坏。

一、部署 MinIO 作为 S3 仓库

先在备份服务器准备目录:

1
2
3
sudo mkdir -p /opt/minio/{data,config}
sudo useradd -r -s /usr/sbin/nologin minio || true
sudo chown -R 1000:1000 /opt/minio

docker-compose.yml 示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
services:
  minio:
    image: quay.io/minio/minio:latest
    container_name: minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: "minioadmin"
      MINIO_ROOT_PASSWORD: "请换成至少32位随机密码"
      TZ: "Asia/Shanghai"
    volumes:
      - /opt/minio/data:/data
      - /opt/minio/config:/root/.minio
    ports:
      - "9000:9000"
      - "9001:9001"
    restart: unless-stopped

启动:

1
2
3
cd /opt/minio
docker compose up -d
docker logs -f minio

访问控制台:http://192.168.10.20:9001,创建 bucket:homelab-restic

生产环境建议不要直接使用 root key 给 Restic。用 MinIO Client 创建专用用户:

1
2
3
4
5
docker run --rm -it --network host quay.io/minio/mc:latest sh
mc alias set local http://192.168.10.20:9000 minioadmin '你的Root密码'
mc admin user add local restic-backup '再生成一个强密码'
mc admin policy attach local readwrite --user restic-backup
mc ls local

如果只想限制到单个 bucket,可以写更细的 JSON policy,不过 Homelab 初期 readwrite + 独立用户已经比直接暴露 root 凭据好很多。

二、客户端安装 Restic 并初始化仓库

在需要备份的 Linux / Docker 主机安装:

1
2
3
sudo apt update
sudo apt install -y restic
restic version

创建环境文件,避免把密钥写进脚本:

1
2
3
4
5
6
7
8
sudo install -d -m 700 /etc/restic
sudo tee /etc/restic/homelab.env >/dev/null <<'EOF'
export AWS_ACCESS_KEY_ID="restic-backup"
export AWS_SECRET_ACCESS_KEY="你的MinIO用户密码"
export RESTIC_REPOSITORY="s3:http://192.168.10.20:9000/homelab-restic/docker-host-01"
export RESTIC_PASSWORD="Restic仓库加密密码,务必离线保存"
EOF
sudo chmod 600 /etc/restic/homelab.env

初始化仓库:

1
sudo bash -c 'source /etc/restic/homelab.env && restic init'

注意:RESTIC_PASSWORD 是客户端加密密码,MinIO 管理员也不能解密你的备份。这个密码一旦丢失,备份基本等于废数据,建议放进密码管理器,并离线保存一份。

三、备份什么:不要无脑备份整个 /

Homelab 里最值得备份的是配置、编排文件、应用数据和数据库导出,而不是系统缓存、镜像层、临时文件。

一个 Docker 主机可以这样组织:

1
2
3
4
5
/opt/stacks/                 # docker compose 项目
/opt/appdata/                # 应用持久化目录
/opt/backup-dump/            # 数据库导出临时目录
/etc/                         # 关键系统配置
/usr/local/bin/               # 自写脚本

排除文件 /etc/restic/excludes.txt

1
2
3
4
5
6
7
8
9
/var/lib/docker/overlay2
/var/lib/docker/image
/var/lib/docker/containers/*/*-json.log
/tmp
/var/tmp
/cache
node_modules
*.tmp
*.cache

备份脚本 /usr/local/sbin/restic-homelab-backup.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env bash
set -euo pipefail

source /etc/restic/homelab.env
HOST_TAG="docker-host-01"
DUMP_DIR="/opt/backup-dump"
mkdir -p "$DUMP_DIR"

# 示例:备份 PostgreSQL 容器数据库,按实际容器名修改
if docker ps --format '{{.Names}}' | grep -q '^postgres$'; then
  docker exec postgres pg_dumpall -U postgres > "$DUMP_DIR/postgres-all.sql"
fi

# 示例:备份 MariaDB / MySQL
if docker ps --format '{{.Names}}' | grep -q '^mariadb$'; then
  docker exec mariadb mariadb-dump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD" > "$DUMP_DIR/mariadb-all.sql"
fi

restic backup \
  /etc \
  /usr/local/bin \
  /opt/stacks \
  /opt/appdata \
  "$DUMP_DIR" \
  --exclude-file /etc/restic/excludes.txt \
  --one-file-system \
  --tag "$HOST_TAG"

# 保留策略:近 7 天每天、近 4 周每周、近 12 个月每月
restic forget \
  --tag "$HOST_TAG" \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 12 \
  --prune

# 快速校验仓库结构;深度校验可以放到周任务
restic check

授权:

1
2
sudo chmod 700 /usr/local/sbin/restic-homelab-backup.sh
sudo /usr/local/sbin/restic-homelab-backup.sh

这里有一个关键点:数据库不要只备份裸 volume。PostgreSQL、MySQL 这类数据库在运行时直接复制数据目录,可能得到不一致状态。更稳妥的方式是先 dump,再让 Restic 备份 dump 文件。大数据库可以用物理备份工具,但 Homelab 多数场景 dump 足够。

四、用 systemd timer 自动化

创建 service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# /etc/systemd/system/restic-homelab-backup.service
[Unit]
Description=Restic Homelab Backup
Wants=network-online.target
After=network-online.target docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-homelab-backup.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7

创建 timer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# /etc/systemd/system/restic-homelab-backup.timer
[Unit]
Description=Run Restic Homelab Backup Daily

[Timer]
OnCalendar=*-*-* 03:30:00
RandomizedDelaySec=20m
Persistent=true

[Install]
WantedBy=timers.target

启用:

1
2
3
4
sudo systemctl daemon-reload
sudo systemctl enable --now restic-homelab-backup.timer
systemctl list-timers | grep restic
journalctl -u restic-homelab-backup.service -n 100 --no-pager

Persistent=true 的好处是:如果凌晨 3:30 主机没开机,下一次开机后会补跑一次,比普通 crontab 更适合偶尔关机的 Homelab。

五、恢复演练:每个月至少跑一次

只会备份不会恢复,等于没备份。常用命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
source /etc/restic/homelab.env

# 查看快照
restic snapshots

# 查看某个快照里的文件
restic ls latest | less

# 恢复单个目录到临时位置
sudo mkdir -p /tmp/restore-test
restic restore latest --target /tmp/restore-test --include /opt/stacks

# 挂载仓库浏览,适合找文件
sudo mkdir -p /mnt/restic
restic mount /mnt/restic

恢复 Docker 应用时,建议流程是:

1
2
3
4
5
cd /opt/stacks/你的应用
docker compose down
rsync -aH --delete /tmp/restore-test/opt/appdata/你的应用/ /opt/appdata/你的应用/
docker compose up -d
docker compose logs -f

数据库恢复示例:

1
2
3
4
5
# PostgreSQL
cat /tmp/restore-test/opt/backup-dump/postgres-all.sql | docker exec -i postgres psql -U postgres

# MariaDB
cat /tmp/restore-test/opt/backup-dump/mariadb-all.sql | docker exec -i mariadb mariadb -uroot -p

踩坑记录

❌ 1. MinIO 和源数据放同一块盘

这只能防误删,不防硬盘坏。最低要求是不同物理盘;更推荐另一台机器、NAS 或异地对象存储。

❌ 2. 只做 forget 不做 prune

restic forget 只是标记旧快照不再保留,真正释放空间需要 --prune。如果担心每日任务太慢,可以每日 forget,每周单独跑:

1
2
3
source /etc/restic/homelab.env
restic prune
restic check --read-data-subset=10%

❌ 3. 备份 Docker overlay2

/var/lib/docker/overlay2 体积巨大且恢复价值低,Restic 会浪费大量时间扫描。真正要备份的是 compose 文件、环境变量模板、应用持久化 volume 和数据库 dump。

❌ 4. 忘记保存 Restic 密码

MinIO access key 可以重置,但 Restic 仓库密码丢了无法解密。建议把 /etc/restic/homelab.env 中的 RESTIC_PASSWORD 放进密码管理器,并打印一份离线保存。

❌ 5. 从不做恢复测试

建议每月创建一个临时目录恢复 latest,至少验证:

1
2
3
restic check
restic restore latest --target /tmp/restore-test --include /etc/hostname
diff /etc/hostname /tmp/restore-test/etc/hostname

进阶:异地副本与不可变备份

如果预算允许,可以再加一层:

方案成本防护能力适合场景
本机 MinIO防误删 ✅入门测试
独立 NAS / 另一台机器防主机损坏 ✅常规 Homelab 🏆
云端 S3 / B2 / R2防本地灾难 ✅重要数据
对象锁 / WORM较高防勒索 ✅家庭照片、文档

rclone 同步 MinIO bucket 到云端也可以,但要注意:Restic 仓库是大量小对象和索引文件,复制时必须保证完整性,复制后最好在目标端跑一次:

1
RESTIC_REPOSITORY="s3:https://你的云端S3/homelab-restic/docker-host-01" restic check

总结

这套 Restic + MinIO 方案适合补齐 Homelab 的“文件级备份”短板:

需求推荐做法
PVE VM 整机恢复PBS / vzdump
配置文件、Compose、脚本Restic 文件级备份 🏆
数据库先 dump,再 Restic
快速误删恢复ZFS snapshot / Restic restore
防硬盘损坏备份到独立物理设备
防本地灾难再同步一份异地

一句话:快照负责回滚,Restic 负责可移植恢复,异地副本负责兜底。 Homelab 数据量可以不大,但恢复链路一定要完整;否则备份只是心理安慰。

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计