为什么个人站长需要 Syncthing,而不是又一个 rsync 脚本
如果你同时管着两三台服务器——一台跑主站,一台做备份,一台当测试环境——你一定写过这样的脚本:几百行 rsync,配上一堆 --exclude,塞进 crontab,每天凌晨三点跑一次。刚开始很香,跑上三个月就开始出问题:某次同步到一半网络断了,脚本没写错误处理,第二天你发现配置目录被同步成了半截;再后来你发现两台机器互相覆盖,A 上的新文件被 B 上的旧文件盖掉,只能靠翻日志定损。
rsync 是"我主动推"的模型,它没有状态、没有冲突检测、没有双向能力。而 Syncthing 的定位完全不同:它是一个常驻的、去中心化的文件同步守护进程,两台机器之间维持一条长连接,任何一方文件一有变化,另一方在几秒内就能收到。它不是用来做"一次性搬家的",而是用来做"持续保持一致"的。
对个人站长来说,Syncthing 有三个真实价值:多机同步网站代码和配置、把本地开发目录实时推送到测试机、以及给关键配置文件做"活备份"——主站挂了,备份机上那份永远是最新的。它不开公网端口,走中继+打洞,被墙的概率也比自建 rsync over SSH 的固定端口低得多。
安装:比你想的简单,但有两个坑要先说
Syncthing 官方提供静态二进制,绝大多数 Debian/Ubuntu 机器上直接跑就行。先下载和解压:
cd /usr/local/bin
VER=v1.27.12
curl -fsSLO https://github.com/syncthing/syncthing/releases/download/v${VER}/syncthing-linux-amd64-${VER}.tar.gz
tar xzf syncthing-linux-amd64-${VER}.tar.gz --strip-components=1 syncthing-linux-amd64-${VER}/syncthing
./syncthing --version第一个坑:不要用 root 跑 Syncthing。它的 Web 管理界面默认监听 8384 端口,如果用 root 跑并图省事直接暴露到公网,等于把你整台机器的文件系统管理权限挂在了外网上。正确做法是建一个专用用户:
useradd -r -m -d /var/lib/syncthing -s /usr/sbin/nologin syncthing
mkdir -p /var/lib/syncthing && chown -R syncthing:syncthing /var/lib/syncthing第二个坑:同步目录的权限。如果你要让 Syncthing 同步 /var/www/html 这种 root 拥有的目录,专用用户是读不了的。别急着 chmod 777,而是把目录属组改成要和 Nginx/PHP 共享的那一组,或者干脆让 Syncthing 只同步 /var/lib/syncthing/sync/www,再用软链接接到站点根目录。
用 systemd 把它变成正经服务
手动 ./syncthing 前台跑只能用于调试,线上必须交给 systemd 托管。Syncthing 自带参数 -no-restart 和 -home,注意 -home 要显式指定,否则它会去找当前用户的默认配置目录,而 nologin 用户的 home 定位经常出乎意料。写一个 unit:
[Unit]
Description=Syncthing - Open Source Continuous File Synchronization
After=network.target
Documentation=man:syncthing(1)
[Service]
User=syncthing
Group=syncthing
ExecStart=/usr/local/bin/syncthing serve --no-browser --no-restart --logflags=0 --home=/var/lib/syncthing
Restart=on-failure
RestartSec=10
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
# 安全加固
ProtectSystem=full
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target这里 SuccessExitStatus=3 4 是关键——Syncthing 用退出码 3 和 4 表示"被要求重启/升级",属于正常退出,如果不声明,systemd 会把它标记为失败并触发邮件告警。ProtectSystem=full 把 /usr、/boot、/etc 设为只读,能挡住不少越权写入。
systemctl daemon-reload
systemctl enable --now syncthing
systemctl status syncthing --no-pager配对两台机器:别急着把 8384 开到公网
Web 界面默认只监听 127.0.0.1:8384,这是对的。要远程管理,走 SSH 隧道,不要改 <address>0.0.0.0:8384</address>:
# 在你本地机器上执行,把远端 8384 映射到本地 8384
ssh -L 8384:127.0.0.1:8384 root@your-server然后用浏览器打开 http://127.0.0.1:8384。首次进入在"操作 → 设置 → 图形用户界面"里给 Web 界面设一个用户名密码,别空着。
加设备的方式:在 A 机的界面点"添加远程设备",把 B 机的设备 ID(界面右下角"操作 → 显示 ID"里那串 56 位 base32)粘进去。到 B 机确认配对后,连接状态会变成"已连接"。设备 ID 是设备证书的公钥指纹,每次重装系统或删配置目录都会重新生成,这也是为什么它比 IP 更可靠——IP 会变,ID 跟着证书走。
文件夹配置:三份设置决定你后面会不会哭
添加同步文件夹时,界面里有几个选项价值极高,很多人默认下一步就跳过了,事后追悔:
1. 文件夹类型。三个选项:仅发送、仅接收、发送与接收。做"主站→备份机"只需在主机设"仅发送"、备机设"仅接收",这样备机上误删误改的文件不会被反向推回主站,这是最安全的生产姿势。只有真正需要双向同步(比如两台编辑机)才用"发送与接收"。
2. 文件版本控制。强烈建议开"简易版本控制"或"回收站式版本控制",保留版本数设 5~10。Syncthing 的删除是会同步的——你在 A 机删了文件,B 机也会删。开了版本控制后,B 机删掉的文件会进 .stversions 目录留一份,这是误删的救命绳。
3. 忽略模式。用 .stignore 排除不该同步的东西——缓存、日志、node_modules、.git 里的大对象。语法和 .gitignore 类似,支持 ! 反选和 (?i) 大小写忽略:
// .stignore —— 放在同步文件夹根目录
(?i)cache
*.log
node_modules
.DS_Store
!config/*.log // 但这个子目录下的日志要同步给网站配置做"活备份"的完整方案
下面是我在用的真实结构。主站和备份机上都跑 Syncthing,备份机设"仅接收",主站设"仅发送":
# 主站 .stignore:只同步配置和代码,不要同步上传目录和缓存
uploads/
cache/
*.log
node_modules/
# 同步范围
/etc/nginx/sites-available
/etc/nginx/nginx.conf
/var/www/html/current/app
/root/scripts重点:不要让 Syncthing 直接同步 /etc/nginx 整目录到生产在跑的机器上并期待它自动 reload。 Syncthing 只负责把文件搬过去,重载要另做。一个干净的做法是在备份机上放一个 inotifywait 监视同步目录,文件变了才触发校验(nginx -t)再 reload:
#!/bin/bash
# /root/scripts/watch-nginx-conf.sh —— 跑在备份机
while inotifywait -r -e modify,create,delete /var/lib/syncthing/sync/nginx-conf; do
if nginx -t 2>&1 | grep -q 'syntax is ok'; then
systemctl reload nginx
echo "$(date) reloaded ok" >> /var/log/st-nginx-reload.log
else
echo "$(date) config invalid, skip reload" >> /var/log/st-nginx-reload.log
fi
done注意那句 nginx -t 校验——永远不要在没有校验的情况下 reload,否则一份写坏的配置同步过来,直接把线上打挂。
中继、打洞与连接质量
Syncthing 默认开启全局发现和中继。两台机器如果都能主动出网(即使没公网 IP),它会尝试 NAT 打洞建直连;打不通才走官方中继,此时速度受中继带宽限制。想强制或优化:在"设置 → 连接"里,把 自建中继地址 留空用默认即可;如果两台机器在同一内网,直接在两边的"设备 → 高级 → 地址列表"里加 tcp://内网IP:22000,走局域网直连,速度能到千兆。
排查连接质量,界面"设备 → 该设备"里能看到"地址"和"连接类型":显示 tcp://... 且非 relay 就是直连,显示 relay:// 就是走中继。日志里 grep 一下 Established secure connection 就能看到每次握手走到哪一步:
journalctl -u syncthing -f | grep -Ei 'relay|nat|established|connect'同步速度为什么上不去?三个真实瓶颈与调法
很多人第一次用 Syncthing 同步一个几 G 的网站目录会遇到同一个抱怨:局域网里也只有几兆每秒。先别怀疑 Syncthing 慢,它默认的限速和扫描策略本身就是保守的,按下面三点逐一排查。
瓶颈一:它默认限速吗?Syncthing 本身默认不限速,但如果你在某些精简版里被人预置了 <maxSendKbps>,就会卡住。在"设置 → 连接"里把收发限速都设为 0(表示不限),重新连接后测速会立刻变化。
瓶颈二:走中继还是直连。前面说过,relay:// 会受中继带宽限制,通常只有几百 KB 到几 MB。要强制走内网直连,在两台同内网机器的"设备 → 高级 → 地址列表"里都加上 tcp://内网IP:22000,并在全局"设置 → 连接"里勾选启用 tcp:// 监听。验证方法还是看连接类型字符串。
瓶颈三:文件太多导致扫描慢,而不是传输慢。如果你的目录里有几十万个 node_modules 小文件,Syncthing 每次扫描都要遍历 inode,CPU 和 IO 会很重,看起来像"卡住"。这也是为什么 .stignore 必须排除 node_modules 和缓存目录——不是省空间,是省扫描时间。可以用 stindex 或看日志里 Completed initial scan 耗时来判断。
另外,文件系统的 inotify 监控也有上限,目录层级过深时 Syncthing 会回退到定时扫描模式,变化感知会变迟。内核参数 fs.inotify.max_user_watches 默认 8192,文件多的时候可以调大到 524288:
echo 'fs.inotify.max_user_watches=524288' >> /etc/sysctl.conf
sysctl -p
# 查看 Syncthing 感知到的扫描/监控状态
journalctl -u syncthing | grep -Ei 'watcher|scan'把 Syncthing 和版本控制结合:别让 .git 被同步
做个人站的人往往同时用 Git 管代码。这时候要和 Syncthing 配合好,否则两边打架。.git 目录里是压缩对象和索引,多台机器同时改动极易冲突,而且体积不小。正确做法是 .stignore 排除 .git,代码同步交给 Git(push/pull),配置文件和上传内容同步交给 Syncthing,各管各的。
如果你实在想让两台机器的工作区一致,更好的方案是用 Syncthing 同步工作文件,用 Git 只做版本快照和回滚,两套系统互不越界。一句话原则:git 管"历史",Syncthing 管"现状"。
监控与告警:让同步状态可观测
Syncthing 挂了,你不会收到任何提示——它静默地不同步,直到某天你发现备份机上的文件停留在两周前。所以要么接监控,要么自己写个探针。最省事的是用它的 REST API,带 API key 请求:
APIKEY=$(grep -oP '(?<=<apikey>)[^<]+' /var/lib/syncthing/config.xml)
curl -s -H "X-API-Key: $APIKEY" http://127.0.0.1:8384/rest/system/status | grep -E 'myID|uptime'
# 查文件夹同步状态:"state":"idle" 才算健康,stalled/error 要告警
curl -s -H "X-API-Key: $APIKEY" http://127.0.0.1:8384/rest/db/status?folder=你的文件夹ID把这段塞进每五分钟跑的脚本,state 不是 idle 或出现 errors > 0 就发通知,就能把"静默失效"变成"主动告警"。这套 REST API 还能查 /rest/db/completion 拿到进度百分比,配合 Grafana 能做出一张同步进度面板。
五个必踩的坑与收尾
坑一:时钟不同步会导致设备证书"未生效"。如果某台机器时间差几分钟,握手会报 certificate not yet valid。先 timedatectl 确认 NTP 正常。
坑二:同步大文件时卡在"同步中 99%"。多半是接收端磁盘满或权限不足,看 journalctl -u syncthing 里的 pull: error 行。
坑三:把 .stfolder 一起同步了。它是 Syncthing 的文件夹标记,绝不能被同步到对端,否则对端会认为这是它自己的文件夹。用 .stignore 排除,或者新建文件夹时别选错根目录。
坑四:多台机器互相同步形成环。如果是 A→B、B→C、C→A 的环形"仅发送/仅接收"配置,会导致更新风暴。星型拓扑最稳:一台中心节点,其余都只跟它同步。
坑五:升级二进制后服务起不来。Syncthing 新版本偶尔会迁移配置格式,升级前备份 /var/lib/syncthing/config.xml 和 cert.pem,出问题能回滚。
最后一句话总结:Syncthing 不是 rsync 的替代品,而是"多机状态一致"这个问题的另一种解法。当你从"每天定时推一次"切换到"任何变化几秒内自动同步"之后,你会发现自己再也不写同步脚本了——配置、代码、备份三件事被一个常驻进程统一管住,这才是个人站长该有的省心状态。