在两台服务器之间同步一个大文件夹(可续传、后台运行)
最近更新: 2026-08-05
你在一台服务器上有一个很大的文件夹——几十 GB——想在另一台上得到一份完整副本。你没法盯着终端好几个小时,而且传到一半断掉的传输不应该从头再来。relayium sync 正是为此而生:一个单向增量镜像,跳过已经存在的文件,把传了一半的文件从中断处续传,并对它发送的每个文件做端到端校验。
本指南搭建一个无人值守、自动恢复的传输:把发送方授权一次,让监听端在后台运行,并在 tmux 里用重试循环驱动 relayium sync,让它在掉线之后继续跑,直到整个文件夹全部到达。
为什么 relayium sync 适合这件事
sync 是走原生协议的单向增量镜像(两端都要装 relayium)。有三个特性让它可以安心地无人值守地反复运行:
- 跳过已存在的文件:接收方上大小和修改时间都一致的文件不会重复发送。
- 续传半截文件:如果某个文件在连接断开时只传了一半,下次运行会从磁盘上已有的字节偏移继续,而不是重新传。
- 校验它发送的内容:它传输的每个文件——包括续传的那些——都会按发送端的 SHA-256 做端到端校验,对不上就报为失败。被跳过的文件只按大小和 mtime 判定,不会重新计算哈希,所以 sync 对它没有发送的内容不作任何保证。
- 正因如此,这条命令是幂等的——再跑一次只做剩下的活,这正是重试循环能完成一次超大传输的关键。
前提条件
你需要准备
- 本指南用 daemon 直连(relayium://),所以两台服务器之间不需要 SSH 互访。
- 在接收方的防火墙或安全组上,向发送方开放监听端口(默认 9031)。
- 接收方要有装得下整个文件夹的空间。开始一场几个小时的传输之前,先把发送方的 du -sh /root/workspace 和接收方的 df -h /root 对一下。
两端服务器都要装 relayium(sync 走原生协议,因此每一端都必须有):
# 两台服务器上都执行
curl -fsSL https://relayium.com/install.sh | sh
把发送方授权一次(在接收方)
接收方对发送的那台机器授权一次;授权会写入磁盘并在重启之间保持有效,因此你无需重复。先在终端里前台启动监听端,并把 --dir 指向父目录——relayium sync /root/workspace 会在接收方重建出 workspace/...,所以 --dir /root 会把文件落到 /root/workspace/。
当发送方首次连接时(见下一节),serve 会显示它的地址和指纹并请你批准;回答 y,它就会被永久记住:
# 在接收方(前台运行,以便交互式批准)
relayium serve --dir /root --port 9031
# 在接收方,首次连接时:
Incoming push from 203.0.113.9:52140
fingerprint: 9f2c41ab…
Accept and remember this peer? [y/N] y
- 若要完全无人值守,可跳过提示:在发送方运行 relayium id 打印其指纹,再在接收方运行 relayium authorize <指纹>。
- --dir 是你要同步的文件夹的父目录,而不是文件夹本身——否则文件会多落一层(例如 /root/workspace/workspace)。
让监听端在后台运行(在接收方)
指纹授权之后,停掉前台的 serve(Ctrl-C),再以脱离终端的方式重新启动,让它在你退出登录后依然存活。它会读回已保存的指纹并静默接受发送方——这次不再提示。同一行还会把新进程的 PID 记进 ~/relayium-serve.pid,本指南最后一步正是靠它停掉自己启动的这个监听端,而不是按名字匹配机器上所有 relayium 命令:
# 在接收方
nohup relayium serve --dir /root --port 9031 > ~/relayium-serve.log 2>&1 & echo $! > ~/relayium-serve.pid
- serve 一次处理一个连接并持续运行,因此下面重试循环的每次重连它都准备好接收。
- 若要做常驻收件箱,改用 systemd 运行(Restart=always,--config-dir /etc/relayium)。
在 tmux 里用重试循环运行 sync(在发送方)
长时间的传输总会被打断——会话断开、网络抖动、机器重启。解决办法不是什么高级工具,而是一个把 sync 反复重跑到成功为止的循环,再加一个终端复用器让它在你退出登录后存活。这里 tmux 比 nohup 更省心:没有容易写错的输出重定向,而且可以重新接入查看进度。
开一个 tmux 会话,然后在 until 循环里运行镜像——它每 10 秒重试一次,直到 sync 成功返回,然后自己退出:
在发送方开一个 tmux 会话,好让循环活得比你启动它的那次 ssh 更久。
# 在发送方 tmux new -s xfer # 没有 tmux 就 apt install -y tmux把镜像放进一个 until 循环里跑。它每 10 秒重试一次,直到 sync 返回成功,然后自己退出。
until relayium sync /root/workspace relayium://203.0.113.43:9031; do echo "$(date) retrying"; sleep 10; done按 Ctrl-b 再按 d 脱离。循环照跑不误;想看的时候随时重新接入。
tmux attach -t xfer
一次成功的传输是什么样
每一趟都会为它真正发出去的每个文件打印一行,然后打印一行汇总:这次发了多少,接收方已经有多少。循环会在 sync 第一次返回成功时结束,而那行汇总就是镜像的状态。
relayium sync /root/workspace relayium://203.0.113.43:9031
workspace/data/part-004.bin (1073741824 bytes)
synced: 1 sent, 812 unchanged- 每次重试做的活都更少:已传完的文件被跳过,传了一半的文件续传——所以循环会收敛并结束。
- 进度是每传完一个文件打印一行,因此一个大文件会安静地传到完成为止。没有输出不代表卡住(见排错)。
验证与收尾
当 until 循环结束、你回到普通的 shell 提示符时,传输就完成了。确认两边一致,然后停掉监听端:
等 until 循环自己退出。回到普通的 shell 提示符,说明的是传输结束了,而不是你把它打断了。
在两台服务器上比较总量。
# 在两台服务器上比较总量 du -sh /root/workspace总量对上之后,在接收方停掉监听端。它只对启动时记下的那个 PID 发信号,而不是拿字符串去匹配机器上每一条 relayium 命令;只有信号成功送出,PID 文件才会被删掉。上一次运行遗留的 PID 文件,可能指向一个已被系统重新分配的 PID,所以拿不准这个文件还是不是你的时,先把那个 PID 的命令行打出来,确认回显的是 serve 再动手。
# 在接收方,确认无误后 ps -p "$(cat ~/relayium-serve.pid)" -o command= kill "$(cat ~/relayium-serve.pid)" && rm ~/relayium-serve.pid
镜像完成是什么样
两台服务器上的 du -sh 报出同一个总量,而且 until 循环已经把你送回普通的 shell 提示符,而不是又在重试。总量一致只是一次粗略的完整性核对,并不能证明内容无误:sync 是按大小和 mtime 决定哪些文件不发送的,所以总量说明不了被跳过文件的内容。
# the same total, on BOTH servers
46G /root/workspace- sync 发送过的内容是校验过的:它传输的每个文件在到达时都做过 SHA-256 校验,干净退出意味着没有一项校验失败。被跳过的部分只比对了大小和 mtime,所以 du -sh 总量一致只是对完整性的一次粗略核对,并不能证明被跳过文件的内容仍然一致。需要这种证明,就在两台服务器上逐个文件比对校验和。
排错
跑几个小时的镜像会碰到六件事。其中三件看着像故障其实不是;另外三件是真问题。每一件都有一条命令能告诉你你正面对的是哪一种。
现象、检查、修复
- 很久没有任何输出,传输看着像卡住了。
# 在发送方,隔几秒跑两次 ss -tinp dst :9031 # ESTAB 说明够得着监听端,但不能证明字节在动 # SYN-SENT 连不上监听端进度只在一个文件传完时才打印,所以一个大文件会在完全的安静中传输。ESTAB 只能证明够得着——一条已建立的连接照样可以闲着或者卡住——它本身永远不能证明还在往前走。隔几秒把这条检查跑两次,比较 -i 为该套接字打印的 bytes_acked 计数:数字在涨就是真的在传,一直不变才是真卡住。
- 套接字停在 SYN-SENT,传输压根没开始。
# 在接收方 sudo ufw allow from 203.0.113.9 to any port 9031 proto tcp ss -tlnp | grep 9031端口被挡住了。只对发送方开放 9031/TCP——把 203.0.113.9 换成发送方自己的地址,公网 IP,或者两台服务器同处一个内网时的内网 IP——云安全组也收敛到同一个来源,然后确认 serve 真的在监听。这是「传输迟迟不开始」最常见的原因。
- 把后台命令粘进去之后,shell 停在一个 > 续行提示符上。
tmux new -s xfer until relayium sync /root/workspace relayium://203.0.113.43:9031; do echo "$(date) retrying"; sleep 10; done带引号和 > 重定向的多行 nohup 命令,粘贴时通常会在重定向那里断掉。改用 tmux 加这条单行循环:没有重定向可写错,而且随时能重新接入观察。
- 清理进程时误杀了不该杀的东西。
pgrep -af relayium ps -p "$(cat ~/relayium-serve.pid)" -o command= tmux kill-session -t xfer kill "$(cat ~/relayium-serve.pid)" && rm ~/relayium-serve.pid按命令行模式去杀,会对所有命令行里含这段文字的进程都发信号;机器上同时跑着不止一个传输时,杀掉的就不是你想杀的那个。而且它也停不下镜像:sync 是 until 循环的子进程,杀掉之后循环十秒后又拉起一个。先用 pgrep -af 看清楚,再去结束本指南真正拥有的东西——tmux kill-session -t xfer 结束那个循环,对 ~/relayium-serve.pid 里的 PID 执行 kill 结束你启动的监听端。如果那个 PID 文件是上一次运行留下的,先把它的命令行打出来再发信号:被系统重新分配出去的 PID,属于完全不相干的另一个进程。
- 你想排除某个子目录,但没有 exclude 参数。
relayium sync /root/workspace/src /root/workspace/data relayium://203.0.113.43:9031sync 支持 -i 和 -p、--delete、--watch 和 --config-dir——没有任何能在树中间过滤掉某个路径的开关。改成直接点名你真正想要的子目录:每个源都会按自己的名字落在接收方的 --dir 下面,所以配合 serve --dir /root/workspace,这条命令会重建 /root/workspace/src 和 /root/workspace/data,而那个可以重新生成的 venv 根本不会被遍历。
- 你以为会镜像过去的源,传过去却是空的。
relayium sync ./links relayium://203.0.113.43:9031 # warning: no regular files to send (symlinks and special files are skipped)sync 只传普通文件,而上面这行警告正是「整棵树全是符号链接」的样子。把 sync 指向这些链接真正指向的目录,接收方需要的符号链接单独处理。
常见问题
传到一半被打断会怎样?
什么都不会丢。重新运行 relayium sync——它会跳过接收方已有的文件,并把传了一半的文件从磁盘上已有的字节偏移续传。本指南里的 until 循环会自动做这件事,直到整个文件夹镜像完成。
这和 rsync 有什么不同?
两者都做增量单向镜像,但 relayium sync 走证书固定的 TLS 连接、不需要 SSH 账号(daemon 直连),通过证书指纹互相认证两台机器,并对它传输的每个文件做 SHA-256 校验。和 rsync 的默认行为一样,接收端上大小和 mtime 已经一致的文件会被跳过,而不是重新计算哈希。它和 relayium 其他模式用的是同一个传输引擎。
sync 会把我从源端删掉的文件在接收方也删掉吗?
只有你要求时才会。默认情况下 sync 只新增和更新。加 --delete 才会镜像删除,而且接收方必须以 --allow-delete 运行 serve 才会执行——否则删除会被忽略并回报给你。
我能让两个文件夹持续保持同步吗?
可以。加 --watch,sync 会持续运行,在源目录下任何改动时重新镜像。对于一次性搬运一个大文件夹,你不需要它——重试循环加一个普通的 sync 就够了。
我必须开一个端口吗?
用 daemon 直连的话,是的——监听端口(默认 9031)必须能从发送方访问到。如果你不想开端口、而且两台服务器之间已经有 SSH,sync 也可以走 SSH:relayium sync /path user@host:/path(远端必须装了 relayium)。
在你自己的两台服务器之间镜像一个文件夹——增量、可续传、无需盯守。
获取 CLI