给 rsync 配一个网页界面:在浏览器里建备份任务、看实时进度、翻历史日志,定时自动跑。
纯 Python 标准库实现(无任何 pip 依赖,镜像约 47MB),内置 rsync,宿主机不用装任何东西。
docker run -d --name rsync-backup-ui --restart unless-stopped \
-p 8765:8765 -e TZ=Asia/Shanghai \
-v "$PWD/rsync-ui-data:/data" \
-v /data-4t:/data-4t:ro \
-v /backup-4t:/backup-4t \
martin0313/rsync-backup-ui:latest
打开 http://NAS_IP:8765,首次访问设置管理员密码即可。
--delete 语义,但被删除(以及被新版本覆盖)的旧文件先移入目标目录的 .deleted/<时间戳>/,不做直接删除| 项 | 值 |
|---|---|
| 端口 | 8765 |
| 数据卷 | /data(任务配置、认证信息、运行日志) |
| 环境变量 | TZ,默认 Asia/Shanghai |
| 内置 rsync | 3.5.0 |
| 运行用户 | root(-a 保留属主/权限需要) |
| 健康检查 | 自带,探测 /api/auth/status |
| 变量 | 默认值 | 说明 |
|---|---|---|
TZ | Asia/Shanghai | 影响调度时间与日志时间戳 |
/data 下有三个文件,外加日志目录:
/data/tasks.json 任务配置 + 历史记录
/data/auth.json 口令哈希(PBKDF2,盐值随机)
/data/settings.json 全局设置(挂载点白名单等)
/data/logs/<task>/ 每轮运行的完整 rsync 输出
这个目录一定要挂出来持久化,否则容器重建后任务和密码全丢。备份它就是备份全部配置(拷走 *.json 与 logs/ 即可)。
任务里填的源/目标路径是容器内的路径,宿主机对应目录必须以相同路径挂进容器:
volumes:
- ./data:/data # 配置持久化
- /data-4t:/data-4t:ro # 源:只读更安全
- /backup-4t:/backup-4t # 目标:rsync 要写这里
然后任务里填 /data-4t/immich_data → /backup-4t/immich_data,就和你直接在宿主机敲 rsync 的效果一致。
写成 -v /data-4t:/src 再去任务里填 /data-4t/immich_data 是不行的:容器里没有这个路径,任务会直接报"源路径不存在"。
services:
rsync-backup-ui:
image: martin0313/rsync-backup-ui:latest
container_name: rsync-backup-ui
restart: unless-stopped
ports:
- "8765:8765" # 只给本机用就写 "127.0.0.1:8765:8765"
environment:
- TZ=Asia/Shanghai
volumes:
- ./data:/data
- /data-4t:/data-4t:ro
- /backup-4t:/backup-4t
备份工具的破坏力比一般应用大,所以下面这些是硬编码在程序里的,不靠"记得别点错":
--delete 配合 --backup-dir,被删除/被覆盖的旧文件保留最近 7 轮,可随时捞回来;轮转只回收真正存了内容的轮次,失败或中断留下的空壳不占名额--delete(含 --del、--delete-*)、--filter/-f、--remove-source-files、--ignore-errors、-e/--rsh、--backup-dir 等高危选项不允许填——镜像语义只能由「镜像模式」开关统一构造,避免绕过 .deleted 保护;保存和启动运行时都会校验,改版前存下的旧配置也不会漏网每轮备份前会先跑一次 rsync -n --stats(只读,不改任何文件)算出"本轮需要传输多少字节",界面进度 = 已传输 / 需传输,所以增量备份也能走到 100%。
日志里 rsync 自带的百分比则是另一个口径:分母是整个文件清单的大小,增量备份只传变化部分,所以跑完可能只显示 36%——这是正常的,日志末尾出现 # 完成: 退出码 0 即表示成功。
代价是每轮会多一次源目录遍历(只读元数据,不读文件内容)。几万个文件通常是秒级。
Q:任务报"源路径不存在"
路径要填容器内路径,并按上文做同名挂载;容器里的路径不会自动等于宿主机路径。
Q:报"挂载点未挂载"
设置里登记了挂载点白名单,而对应磁盘当前没挂上。插好盘/恢复挂载后重试,或在设置里改白名单。
Q:日志最后停在 30%~40%
见上一节,rsync 自带百分比的分母不同,不是中断。以 # 完成: 退出码 0 和 total size is ... speedup is ... 为准。
Q:目标文件属主/权限不对
rsync 以 root 运行并用 -a 保留属主与权限,这是多数 NAS 场景的预期行为。如果要按指定 UID 运行,加 --user $(id -u):$(id -g),同时确保 /data 和目标是该 UID 可写。
Q:能不能备份到另一台机器(SSH)?
当前版本只做本机目录之间同步,路径必须是本地绝对路径,-e/--rsh 也在禁用列表里。远端备份请先挂载(NFS/SMB/sshfs)再备份。
Q:能上 HTTPS 吗?
本服务是明文 HTTP,认证 Token 走请求头。只在局域网用问题不大;要跨网访问请走反向代理 + TLS 或 SSH 隧道,不要直接暴露到公网。
Q:升级会不会丢配置?
不会。配置与历史都在 /data 卷里,docker compose pull && docker compose up -d 换完镜像后原样保留。跨大版本升级前备份一下 /data 更稳妥。
| 标签 | 说明 |
|---|---|
latest | 最新构建(当前等同 20261103) |
20261103 | 安全加固版:镜像保护链加固(禁止额外参数绕过 .deleted)、空源目录拦截、轮转只回收非空轮次、设置页重做、新增 favicon |
20261102-compact | 紧凑版界面(正文 14px,可一键切回标准版) |
20261102 | 进度修复版(标准界面) |
20261015 | 更早的稳定版本 |
cd rsync-backup # 项目根目录(含 Dockerfile 与 docker-compose.yml)
docker compose build
docker compose up -d
镜像只有一个 Dockerfile:python:3.13-alpine + apk add rsync tzdata,代码在 web/(server.py + static/),无任何 pip 依赖。
个人自用工具,未附带开源许可证。
Content type
Image
Digest
sha256:3259e7ef7…
Size
16.9 MB
Last updated
2 days ago
docker pull martin0313/rsync-backup-ui