服务卡片 · 文件与存储
把 MinIO 当作备份目标:机架上的 S3,给那些只认它的工具用
这台机架上的备份去三个地方:NAS、一个加密的 restic 仓库、以及异地。MinIO 存在的理由是,越来越多的备份工具只讲 S3、别的都不讲——restic、Duplicati、Immich 和 Nextcloud 的备份脚本,以及那个只接受 S3 端点的容器备份工具。在这里跑一个,就意味着这些工具能拿到它们想要的接口,而不需要在别处开账号。
| 容器镜像 | minio/minio:RELEASE.2026-01-15T00-00-00Z |
|---|---|
| 宿主端口 | :9000/tcp, :9001/tcp |
| 数据目录 | /srv/homelab/minio/data |
| 内存预留 | 340 MB |
| CPU 上限 | 0.50 vCPU (cpus: "0.50") |
| 更新节奏 | quarterly, and the version string is date-based. The project publishes a community release and the AGPL terms changed once already, so the pinned tag matters more here than anywhere else on the rack |
| ARM 兼容 | arm64 native; erasure coding across multiple drives is not used here because a single-drive setup has nothing to code against |
| 宿主 | 容器 | proto | 暴露 | 用途 |
|---|---|---|---|---|
| :9000 | 9000 | tcp | 仅局域网 | the S3 API that backup tools and applications write to |
| :9001 | 9001 | tcp | 仅局域网 | the web console, used for browsing buckets and issuing access keys |
部署步骤
在首次启动前替换默认 root 凭据
镜像拒绝用文档里那套默认值启动,这是刻意的安全设计。把两个值都生成出来,之后再也不要把它们手打进工具配置里。
run echo "MINIO_ROOT_USER=rackroot" >> /srv/homelab/.env echo "MINIO_ROOT_PASSWORD=$(openssl rand -base64 32 | tr -d '/+=')" >> /srv/homelab/.env chmod 600 /srv/homelab/.env建桶时就启用对象锁定,事后补不了
这是唯一一个必须在写入第一个字节之前定下来的决定。已有的桶之后无法启用对象锁定,只能重建桶、重新初始化仓库。
run cd /srv/homelab/minio && docker compose up -d docker exec -it minio mc alias set rack http://127.0.0.1:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" docker exec -it minio mc mb --with-lock rack/restic-rack给每个写入应用单独签发一个限定范围的密钥
共用一个凭据,意味着四个工具里任何一个都能删掉一切。限定范围的密钥能把「全盘沦陷」变成「局部失控」,而且配置成本为零。
run docker exec -it minio mc admin user add rack immich-backup "$(openssl rand -base64 24)" docker exec -it minio mc admin policy create rack only-immich-dumps /config/policies/immich.json docker exec -it minio mc admin policy attach rack only-immich-dumps --user immich-backup
桶里到底放什么
四个性格不同的桶。restic-rack 只允许追加,装着每晚备份写入的加密仓库。immich-dumps 装着那些否则就会变成「一个等着被遗忘的本地文件」的数据库导出。nextcloud-cold 装着每季度才写一次的归档导出。tmp-share 就是它听起来的样子——工具们用作暂存区的临时桶,配了一条把超过七天的对象删掉的生命周期规则。
| 桶 | 写入方 | 版本控制 | 生命周期 |
|---|---|---|---|
| restic-rack | 派上的 restic | 启用,对象锁定 | 无,只追加 |
| immich-dumps | Immich 备份任务 | 启用 | 30 天后过期 |
| nextcloud-cold | 季度导出任务 | 启用 | 无 |
| tmp-share | 各类工具 | 关闭 | 7 天后过期 |
对象锁定才是让它成为备份的东西
一个以备份用户身份运行的勒索进程可以删掉备份仓库。带保留期的对象锁定让这些对象在一段确定的时间内不可变,于是删除会失败,仓库得以存活。
前提是对象锁定必须在建桶时启用,之后补不了。这是在写入第一个字节之前就值得定下来的唯一一个设置,因为事后没有任何办法补上它。
mc mb --with-lock rack/restic-rack
mc retention set --default GOVERNANCE 30d rack/restic-rack
mc version enable rack/restic-rack
mc ilm rule add --expire-days 7 rack/tmp-share每个写入方一个密钥,以及为什么
四个工具共用一个 S3 凭据,意味着其中任何一个都能读取和删除全部内容。限定作用域的密钥不花任何代价,却能把「全盘沦陷」变成「局部失控」。
每个密钥拿到的策略只允许访问自己的那个桶,别的都不行——没有账号级的桶列举权限,访问不了控制台。策略文件和其它配置一起放在仓库里。
mc admin user add rack immich-backup "$IMMICH_S3_SECRET"
mc admin policy create rack immich-dumps-only /config/policies/immich.json
mc admin policy attach rack immich-dumps-only --user immich-backup关于性能,说点实话
树莓派上的 MinIO 走千兆链路加 USB 固态盘,写入大约每秒 60 到 90MB——对每晚备份来说完全够用,而对任何对延迟敏感的主存储层来说完全不适合。它是一个讲 S3 的备份目标,不是 NAS 的替代品。
它真正挣到位置的地方是异地复制:MinIO 能把一个桶镜像到另一个兼容 S3 的端点,于是本地一个桶变成两地备份,一条命令的事,不用再添一个备份工具。
mc mirror --watch rack/restic-rack remote/restic-rack
# 或者用定时的单向同步,而不是 watch 循环compose 文件
整份文件放在 /srv/homelab/minio/compose.yaml。标签固定版本号,不用 latest——树莓派上回滚比升级麻烦得多。
services:
minio:
image: minio/minio:RELEASE.2026-01-15T00-00-00Z
container_name: minio
restart: unless-stopped
command: server /data --console-address ":9001"
ports:
- "9000:9000/tcp"
- "9001:9001/tcp"
environment:
TZ: Asia/Shanghai
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
MINIO_BROWSER_REDIRECT_URL: https://objects.lan
MINIO_SERVER_URL: https://objects.lan:9000
volumes:
- /srv/homelab/minio/data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 30s
timeout: 10s
retries: 3
networks: [rack]
networks:
rack:
external: true加固清单
- 默认 root 凭据在首次启动前就被替换;镜像拒绝用文档里那套默认值启动,这是刻意的安全设计
- 每个写入方一个访问密钥,作用域限定在单个桶,被攻陷的备份客户端读不到别的应用的桶
- 控制台在局域网内并置于认证门户之后;S3 API 端口只有写入它的容器能访问
- 存放 restic 仓库的那个桶启用了版本控制和对象锁定,这是让「被勒索删除」可恢复的关键
备份方案
这些桶里装的就是备份数据,再备份一次会变成循环。真正保护的是配置:访问密钥、桶策略和生命周期规则都记录在仓库里,底层卷按和别的东西一样的计划同步到 NAS。
上线后验证
- restic 备份能写入 restic-rack 桶,且 restic check 校验仓库通过
- 在保留期内删除对象会以 governance 错误失败,说明对象锁定生效了
- immich-backup 这个密钥能写自己的桶,尝试列举别的桶时被拒绝
- 网页控制台里四个桶的版本控制和生命周期设置都符合预期
踩过的坑
- 对象锁定不能在已有的桶上启用。如果 restic 仓库是在没有它的情况下建的,就得重建桶、重新初始化仓库,代价是一整个备份周期。
- root 凭据不是任何工具该用的凭据。用 root 配置的工具能删掉每一个桶,这把作用域密钥的意义全抵消了。
- 在 SD 卡上跑 MinIO 会把卡写坏。它是机架上写入最重的服务,和其余数据一样,该待在固态盘上。
- 基于日期的发布标签不遵循语义化版本,而且社区版已经改过一次许可条款。读发布说明,锁定一个具体标签,别追 latest。
硬件与选型问答
- NAS 已经能提供 SMB 了,还需要 MinIO 吗?
- 只有在某个工具要求 S3 时才需要。SMB 是文件系统协议,对任何能挂载共享的东西都适用;S3 是对象 API,带版本控制、对象锁定和生命周期规则,这是文件系统共享给不了的。这里的典型案例就是 restic 仓库:只追加、不可变、有版本,而且必须能被一个只讲 S3 的工具访问。
- 相比一个普通的目录,它增加了多少开销?
- 340MB 内存,多一个要打补丁的服务,以及相比直接写文件系统大约百分之十的吞吐损失。换来的是接口契约:备份工具完全不需要知道机架的文件系统布局,而桶可以被镜像到别处而不改工具配置。
- 能让 NAS 和 MinIO 用同一块卷吗?
- 物理上可以,但这是个坏主意:备份目标的意义就在于它能挺过被备份对象的故障。如果 NAS 的卷和 MinIO 的数据在同一块盘上,一次盘故障会同时带走数据和备份。本机架把它们放在不同设备上,MinIO 的数据在派的固态盘里。