Sign inSign up

martin0313/rsync-backup-ui

By martin0313

•Updated 2 days ago

给 rsync 加网页界面:建任务、看进度、查历史、定时跑

Image
0

227

martin0313/rsync-backup-ui repository overview

⁠Rsync 备份管理

给 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,首次访问设置管理员密码即可。


⁠功能

  • 任务管理:多源目录同步到一个目标,支持排除规则、自定义 rsync 参数
  • 实时进度:百分比 / 已传输量 / 速度 / 剩余时间,秒级刷新,控制台同步滚动
  • 镜像模式:--delete 语义,但被删除(以及被新版本覆盖)的旧文件先移入目标目录的 .deleted/<时间戳>/,不做直接删除
  • 定时调度:每天固定时间、固定间隔(最小 1 分钟)或仅手动
  • 历史与日志:每轮运行落盘日志,界面内查看/下载,每任务保留最近 50 个日志、100 条历史
  • 路径浏览器:只读浏览服务器目录,点选源/目标路径,不用手打
  • 等价命令:任何任务都能一键查看"完全等价的 rsync 命令",方便手动跑或核对
  • 登录保护:PBKDF2-SHA256 口令哈希、连续失败锁定、Token 会话

⁠镜像信息

项值
端口8765
数据卷/data(任务配置、认证信息、运行日志)
环境变量TZ,默认 Asia/Shanghai
内置 rsync3.5.0
运行用户root(-a 保留属主/权限需要)
健康检查自带,探测 /api/auth/status
⁠环境变量
变量默认值说明
TZAsia/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 是不行的:容器里没有这个路径,任务会直接报"源路径不存在"。

⁠docker compose

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

⁠安全设计

备份工具的破坏力比一般应用大,所以下面这些是硬编码在程序里的,不靠"记得别点错":

  • 挂载点白名单:设置里登记挂载点后,如果源/目标所在的磁盘没挂上,任务直接中止——避免 rsync 把几 TB 数据写进根文件系统或容器层(设置页会实时显示每个挂载点当前挂没挂上)
  • 源目录非空校验:镜像模式下源目录为空时直接中止,防止源盘没挂上时把备份端一起清空(确认确实为空时,可在任务里勾选「允许源目录为空」放行)
  • 路径校验:源与目标相同、互为父子目录、源目录互相嵌套,一律拒绝
  • 磁盘空间保护:启动前检查目标剩余空间(默认 10GB,可按任务调整),运行中每 60 秒复查,低于阈值立即终止
  • 删除可回滚:--delete 配合 --backup-dir,被删除/被覆盖的旧文件保留最近 7 轮,可随时捞回来;轮转只回收真正存了内容的轮次,失败或中断留下的空壳不占名额
  • 额外参数黑名单:--delete(含 --del、--delete-*)、--filter/-f、--remove-source-files、--ignore-errors、-e/--rsh、--backup-dir 等高危选项不允许填——镜像语义只能由「镜像模式」开关统一构造,避免绕过 .deleted 保护;保存和启动运行时都会校验,改版前存下的旧配置也不会漏网
  • 并发保护:同一任务不会同时跑两份,服务重启会先回收 rsync 进程组

⁠关于进度百分比

每轮备份前会先跑一次 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 依赖。


个人自用工具,未附带开源许可证。

Tag summary

Content type

Image

Digest

sha256:3259e7ef7…

Size

16.9 MB

Last updated

2 days ago

docker pull martin0313/rsync-backup-ui