ZeroTier自建Planet服务器 IP 变更后,完整恢复旧网络和成员数据

当自建 ZeroTier Planet 服务的服务器公网 IP 发生变更时,如果直接重新运行 deploy.shdocker compose up,会生成全新的控制器身份和 planet 文件,导致旧客户端无法连接,所有历史网络、成员授权记录全部丢失。

本文提供一套精准恢复方案,核心目标是:

保留原有控制器 root identity
保留所有旧虚拟网络(Network ID)
保留成员授权和 IP 分配
仅将 planet / moon 文件中的公网 endpoint 更新为新 IP

最终效果:客户端只需替换一次 planet 文件即可无缝接入,所有历史数据完整保留。

准备工作

  • 新安装目录示例:/root/docker-zerotier-planet/data/zerotier
  • 旧备份目录示例:/root/docker-zerotier-planet/data/backup/zerotier(建议提前完整备份)

必须恢复的关键旧数据

以下文件决定了控制器身份、网络 ID 和成员授权记录,必须从备份中恢复:

  • data/backup/zerotier/one/identity.secret
  • data/backup/zerotier/one/identity.public
  • data/backup/zerotier/one/authtoken.secret
  • data/backup/zerotier/one/current.c25519
  • data/backup/zerotier/one/previous.c25519
  • data/backup/zerotier/one/moon.json
  • data/backup/zerotier/one/controller.d/(存放所有网络和成员信息)

不建议直接恢复的旧生成物(会包含旧 IP):

  • planet*.moon 文件
  • peers.d/moons.d/ 等运行时目录

恢复步骤

1. 停止容器并备份当前状态

1
2
3
4
5
cd /root/docker-zerotier-planet
docker rm -f myztplanet # 替换为您的容器名

# 备份当前新数据,防止操作失误
zip -qr data/zerotier_before_restore.zip data/zerotier

2. 恢复旧控制器身份和网络数据

Bash

1
2
3
4
5
6
7
8
9
10
11
12
# 删除可能冲突的 controller.d
rm -rf data/zerotier/one/controller.d

# 恢复关键文件
cp -a data/backup/zerotier/one/controller.d data/zerotier/one/
cp -a data/backup/zerotier/one/identity.secret data/zerotier/one/
cp -a data/backup/zerotier/one/identity.public data/zerotier/one/
cp -a data/backup/zerotier/one/authtoken.secret data/zerotier/one/
cp -a data/backup/zerotier/one/current.c25519 data/zerotier/one/
cp -a data/backup/zerotier/one/previous.c25519 data/zerotier/one/
cp -a data/backup/zerotier/one/moon.json data/zerotier/one/
cp -a data/backup/zerotier/one/mkworld data/zerotier/one/

3. 补齐容器所需的工具软链接

Bash

1
2
3
ln -sfn ../../../usr/sbin/zerotier-one data/zerotier/one/zerotier-one
ln -sfn ../../../usr/sbin/zerotier-one data/zerotier/one/zerotier-cli
ln -sfn ../../../usr/sbin/zerotier-one data/zerotier/one/zerotier-idtool

4. 清理旧运行状态和旧生成物

Bash

1
2
3
4
5
6
7
8
9
rm -rf data/zerotier/one/peers.d data/zerotier/one/moons.d
mkdir -p data/zerotier/one/peers.d data/zerotier/one/moons.d

rm -f data/zerotier/one/*.moon \
data/zerotier/dist/*.moon \
data/zerotier/dist/planet \
data/zerotier/one/planet \
data/zerotier/one/world.bin \
data/zerotier/one/zerotier-one.pid

5. 使用新 IP 重新生成 planet 和 moon

Bash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
cd /root/docker-zerotier-planet

NEW_IP=$(tr -d '\r\n' < data/zerotier/config/ip_addr4)
IMG=xubiaolin/zerotier-planet:latest

docker run --rm --entrypoint sh \
-e NEW_IP="$NEW_IP" \
-v /root/docker-zerotier-planet/data/zerotier/dist:/app/dist \
-v /root/docker-zerotier-planet/data/zerotier/one:/var/lib/zerotier-one \
-v /root/docker-zerotier-planet/data/zerotier/config:/app/config \
"$IMG" -lc '
set -e
ln -sf /usr/sbin/zerotier-one /tmp/zerotier-idtool

cd /var/lib/zerotier-one
ZT_PORT=$(cat /app/config/zerotier-one.port | tr -d "\r")

[ -s moon.json ] || /tmp/zerotier-idtool initmoon identity.public > moon.json

jq --argjson eps "[\"$$ {NEW_IP}/ $${ZT_PORT}\"]" \
".roots[0].stableEndpoints = \$eps" \
moon.json > moon.json.tmp

mv moon.json.tmp moon.json

rm -f ./*.moon moons.d/*.moon /app/dist/*.moon /app/dist/planet world.bin planet

/tmp/zerotier-idtool genmoon moon.json

mkdir -p moons.d
cp ./*.moon moons.d/

./mkworld

cp world.bin /app/dist/planet
cp world.bin /var/lib/zerotier-one/planet
cp ./*.moon /app/dist/

echo "新 endpoint:"
jq -c ".roots[0].stableEndpoints" moon.json
sha256sum /app/dist/planet /var/lib/zerotier-one/planet
'

重要:data/zerotier/dist/planet 和 data/zerotier/one/planet 两个文件必须完全一致。

6. 修复 ztncui Token(避免 401 错误)

Bash

1
2
TOKEN=$(cat data/zerotier/one/authtoken.secret | tr -d '\r\n')
sed -i "s/^ZT_TOKEN=.*/ZT_TOKEN=${TOKEN}/" data/zerotier/ztncui/src/.env

7. 启动容器

Bash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cd /root/docker-zerotier-planet

ZT_PORT=$(tr -d '\r\n' < data/zerotier/config/zerotier-one.port)
API_PORT=$(tr -d '\r\n' < data/zerotier/config/ztncui.port)
FILE_PORT=$(tr -d '\r\n' < data/zerotier/config/file_server.port)
IPV4=$(tr -d '\r\n' < data/zerotier/config/ip_addr4)
IPV6=$(tr -d '\r\n' < data/zerotier/config/ip_addr6)

docker run -d \
--name myztplanet \
-p $$ {ZT_PORT}: $${ZT_PORT} \
-p $$ {ZT_PORT}: $${ZT_PORT}/udp \
-p $$ {API_PORT}: $${API_PORT} \
-p $$ {FILE_PORT}: $${FILE_PORT} \
-e IP_ADDR4=${IPV4} \
-e IP_ADDR6=${IPV6} \
-e ZT_PORT=${ZT_PORT} \
-e API_PORT=${API_PORT} \
-e FILE_SERVER_PORT=${FILE_PORT} \
-v /root/docker-zerotier-planet/data/zerotier/dist:/app/dist \
-v /root/docker-zerotier-planet/data/zerotier/ztncui:/app/ztncui \
-v /root/docker-zerotier-planet/data/zerotier/one:/var/lib/zerotier-one \
-v /root/docker-zerotier-planet/data/zerotier/config:/app/config \
--restart unless-stopped \
xubiaolin/zerotier-planet:latest

服务端验证

Bash

1
2
3
4
5
6
7
8
9
10
11
# 确认 root ID 是否为旧值
cut -d: -f1 data/zerotier/one/identity.public

# 确认 endpoint 已更新
jq -r '.roots[0].stableEndpoints[]' data/zerotier/one/moon.json

# 查询控制器网络列表(应显示所有旧 Network ID)
docker exec myztplanet sh -lc '
TOKEN=$(cat /var/lib/zerotier-one/authtoken.secret)
wget -qO- --header="X-ZT1-Auth: $TOKEN" http://127.0.0.1:9994/controller/network
'

客户端操作

  • planet 下载地址http://新IP:文件服务器端口/planet?key=xxx
  • moon 下载地址(可选):http://新IP:文件服务器端口/0000000651177f3e.moon?key=xxx

Linux 客户端示例:

Bash

1
2
3
4
5
6
systemctl stop zerotier-one
cp planet /var/lib/zerotier-one/planet
systemctl start zerotier-one

zerotier-cli peers
zerotier-cli listnetworks

正常情况下会显示 PLANET … DIRECT … 新IP/9994。

常见问题与注意事项

  • ztncui 面板打不开:检查 .env 中的 ZT_TOKEN 是否已同步更新。
  • 客户端仍显示旧 IP:确认 planet 文件 MD5 一致,且已重启客户端服务。
  • UDP 连不通:检查防火墙、安全组、运营商 NAT 是否放行 9994/udp 端口。
  • 下次 IP 变更建议:提前准备自动化恢复脚本,或考虑使用域名 + DDNS 方案减少 planet 硬编码问题。