朋友看到幻兽帕鲁(Palworld)出了 1.0,揪我一起玩。他们两年前就破过一轮,想趁这次大改版重破一次,而我那阵子刚好有空。

老实说我原本对这游戏印象不太好,当年那个「抄宝可梦」的争议摆在那里。但后来它的官司打赢了,加上 1.0 的更新幅度是真的大,我想或许没想像中严重,就跟着入手了。

朋友一开始是用 Radmin VPN 把大家串起来连的。这方法不能说不行,但它总让我有点资安上的疙瘩。Radmin 这类虚拟区网走的是 P2P:两端能直连就直连,穿不过对方的 NAT 或防火墙,就退回走它自己的。以我朋友家的网路,我不太相信每组连线都打得穿,大概率会落到中继。中继的代价有两层:一是慢(封包多绕一圈远路),二是游戏流量得经过一家免费闭源服务的伺服器。它确实全程加密,但要不要把连线交给第三方绕一趟,对我来说是个不必要的风险。而我手上刚好有一台 24/7 开着的家用机,趁他们进度还没推多久,我说:不如我来架一台专用伺服器,大家直接连我家。

于是有了这篇。用 Docker 架一个幻兽帕鲁专用伺服器其实不难,docker compose up 几分钟就跑起来了。真正难的是下一步:架好了,朋友却连不进来。 这一关卡住的人,远比卡在「怎么架」的人多。这篇把整条路走一遍,从资源评估、docker-compose,到最容易卡关的那道 UDP 连线,再到「朋友说版本不符进不去」怎么修。所有指令都能照着跑,密码跟 IP 我用占位符,你换成自己的。

先说用哪个 image,和记忆体到底吃多少#

先讲一句:多数人自架其实是用官方的专用伺服器工具(SteamCMD 直接拉 PalServer),不一定要 Docker。 我走 Docker 纯粹是自己用习惯了、跟机器上其他服务一致好管理;如果你也有一台常开的 server、docker 用得顺手,这条路会很舒服。我用的 image 是社群维护的 ,它把 SteamCMD 下载、设定档生成、备份、RCON 都包好了。

记忆体要先破除一个迷思:你查主机商的规格页,清一色写「16GB 起、32GB 更稳」,那数字偏保守(他们也在卖 RAM)。实际上小团队日常很省,我这台 Linux docker 跑 3~5 个人,实测大约落在 1.2GB(没有长时间监控,是随手看的值)。

~1.2GB
我实测(3~5人)
随手看,非长时监控
8~12GB
泄漏累积后
社群实测连跑一周不重启
16GB+
主机商建议
偏保守

那为什么还要在意记忆体?因为幻兽帕鲁有个的老毛病:进程本身会随时间慢慢变肥,跟你们在游戏里盖了多少东西无关。社群实测一个 4 人服连续跑、都不重启,一周内能从 5G 爬到 8~12G。所以真正该做的不是准备 32G,而是设一个 mem_limit 挡住上限、再配每天定时重启(重启会释放记忆体,有人在线就延后),把泄漏压在合理范围——这样它跑再久也不会拖垮同机的其他服务。

一份最小可用的 docker-compose#

核心设定其实不多。下面这份去掉了枝节,留下真正该懂的几行:

yaml
services:
  palworld:
    image: thijsvanloef/palworld-server-docker:latest
    restart: unless-stopped
    container_name: palworld
    stop_grace_period: 30s          # 收到停止讯号后,留 30 秒让它存档
    ports:
      - "8211:8211/udp"             # 游戏连线,UDP(重点,下面详谈)
      - "27015:27015/udp"           # Steam query,UDP
      - "127.0.0.1:25575:25575"     # RCON,只绑本机,外面碰不到
    environment:
      PLAYERS: "16"
      SERVER_NAME: "My Palworld"
      SERVER_PASSWORD: "<你的伺服器密码>"
      ADMIN_PASSWORD: "<你的管理密码>"
      UPDATE_ON_BOOT: "true"        # 容器启动时检查游戏更新
      RCON_ENABLED: "true"          # 自动备份/重启要靠它优雅关机
      BACKUP_ENABLED: "true"
      AUTO_REBOOT_ENABLED: "true"   # 每日重启,释放膨胀的记忆体
    mem_limit: 16g
    volumes:
      - ./data:/palworld            # 游戏本体 + 存档都在这

几个当下不明显、之后会踩到的点:

  • 821127015 后面那个 /udp 不能漏。Palworld 走的是 UDP,漏了写成 TCP,server 起得来、你本机也连得上,但外面的人一个都进不来。
  • RCON 绑 127.0.0.1:它是拿来让自动备份/重启能先广播警告再优雅关机的管理通道,只给本机用,不该对外开。
  • mem_limitAUTO_REBOOT_ENABLED 是一组的,专门对付上面说的记忆体膨胀。

真正卡人的是网路:UDP 连线三关#

docker compose up -d 之后,server 就在 8211 上跑了,你自己在同一个网段连 区网IP:8211 通常没问题。然后朋友一连,连不进来。

因为封包要从网际网路走到你家那个容器,中间有三道关,少一道就进不来:

主机防火墙放行 UDP

先让封包进得了主机。 开两个 UDP port:

bash
sudo ufw allow 8211/udp comment 'Palworld game'
sudo ufw allow 27015/udp comment 'Palworld query'

协定一定是 udp。这是三关里最常被写成 tcp 的一关。

路由器 port forwarding:外部 → 内网,而且是 UDP

封包从网际网路进到你家,要靠路由器把它转发给那台主机。到路由器管理介面加两条转发规则:

外部 (WAN)→ 内部主机协定
8211192.168.x.x : 8211UDP
27015192.168.x.x : 27015UDP
让朋友有个网址可以连:DDNS,而且是灰云

家用网路的公网 IP 通常会变动,所以挂一个 ,让朋友记一个固定网址就好,不用每次问你 IP。如果你用 Cloudflare 管网域,这里有个致命细节在下一段。

WARNING

Cloudflare 要用「灰云」(DNS only),不能开「橘云」(Proxied) Cloudflare 的橘云代理只转发 HTTP/HTTPS 流量,它不转任意的 UDP 封包。你把 Palworld 的网域开成橘云,朋友会 100% 连不上,而且错得很安静(网页类服务照常,只有游戏连不进来,很难联想到是这里)。一定要点成灰云,让网域直接解析到你家 IP。

两个高频误区,先讲清楚免得你白忙:

NOTE

不用改 nginx,一个字都不用动 很多人架网页服务习惯了,一遇到「对外」就想到反向代理。但 ,而 Palworld 是 UDP 游戏封包,走「路由器 → ufw → 容器」直通,跟 nginx 完全无关。这次 /etc/nginx 一个字都不用改。

27015 到底要不要开?它是 ,只影响「能不能在 Steam 的伺服器浏览器看到你的 server 状态」。想极简的话,先只开 8211,朋友直连 你的网址:8211 就能玩;之后真的有人想从 Steam 最爱清单看在线状态,再补 27015

世界设定的坑:直接改 ini 会被盖掉#

server 通了之后,你大概想调一下经验倍率之类的。这里有个一定会踩的坑:这个 image 每次开机,会用环境变数重新生成 PalWorldSettings.ini 你手动去改那个 ini 档,下一次容器重启就被盖回去了,改了像没改。

正确做法是把设定写进 docker-compose.ymlenvironment 区块,让它生成的时候就带上你的值。常见的几项:

常用的世界设定(写进 compose 的 environment)
环境变数效果范例值
EXP_RATE经验值倍率3.0
COLLECTION_DROP_RATE采集掉落倍率3.0
DEATH_PENALTY死亡惩罚Item(只掉道具、装备保留)
PAL_EGG_DEFAULT_HATCHING_TIME蛋孵化时间(小时)1(预设 72)
BASE_CAMP_WORKER_MAX_NUM基地工作 Pal 上限20
SERVER_PLAYER_MAX_NUM人数上限32
PVP / ENABLE_FRIENDLY_FIREPvP / 友军伤害true / false

改完 docker compose up -d,容器重建、用新环境变数重新生成 ini,设定就生效了。要验证有没有吃到,可以进容器看生成出来的 ini(docker exec palworld cat .../PalWorldSettings.ini),确认是你的值再收工。

朋友说「版本不符」进不去:是 server 落后了#

玩了几天,某个朋友回报「进不去,显示版本不符」。这几乎都是同一个原因:他的 client 已经自动更新到新版,而你的 server 还停在旧版。

怎么确认?比对 :本地 server 的 buildid 对上 Steam 最新的 buildid,不一样就是落后了。

这里有个很坑的假象要拆穿:容器日志可能写着 container is up to date,你以为已经是最新——那句话指的是 Docker image 最新,不是游戏档最新。 游戏更新是 UPDATE_ON_BOOT 在容器启动时跑一次 SteamCMD 抓的,如果你的容器已经连续跑了好几天没重启,它就没机会抓到这几天的新版。

修法是重启容器,触发一次 SteamCMD 更新:

bash
docker compose up -d --force-recreate

但重启会把线上的人踢下线,别直接动手。优雅的做法是先用 RCON 广播、存档,再重启:

TIP

更新前先广播 + 存档,别在人家玩到一半直接踢 透过 RCON 送一句广播(「伺服器 5 分钟后更新重启」)给线上玩家、触发一次存档,再 --force-recreate。被踢的人等他们的 client 也更新完(通常自动、几分钟),直接重连就好,存档不受影响。更新约一两分钟(下载 ~5GB),完成后本地 buildid 就跟朋友的 client 对上了。

如果不想每次都手动盯,把 AUTO_UPDATE_ENABLED 打开,server 会定时自己检查更新(一样可以设有人在线先广播再更新),游戏一出新版就不会再卡朋友进不来。

别让 18GB 游戏本体天天进你的备份碟#

最后一个容易忽略的维运陷阱。如果你跟我一样有一套每日 rsync 备份整个伺服器目录的机制,加了 Palworld 之后要注意:那个 data/ 资料夹装着整包游戏本体,将近 18GB,不处理的话它会天天被塞进你的备份碟。

真正需要备份的只有存档,不是那 18GB 游戏档(游戏档随时能重新下载)。所以:

  • rsync 排除掉 palworld/data(游戏本体)
  • 单独备份存档目录(Pal/Saved),那才是删了会心痛的东西
  • 把 Palworld 存档也纳入你既有的旧备份清理回圈(例如只留 7 天)

.gitignore 那边也顺手确认 data/ 有被排除,免得 18GB 游戏档跟着进版控。

一页式清单#

把整条路浓缩成可照抄的顺序:

幻兽帕鲁自架检查清单
  1. 记忆体别被吓到:小团队实测 1~2G 就够(主机商的 16G 偏保守);真正要防的是记忆体泄漏,靠容器 mem_limit + 每日重启压住。
  2. compose:port 记得加 /udp;RCON 绑 127.0.0.1;密码写环境变数。
  3. 网路三关:ufw 放行 udp → 路由器 port forwarding(UDP!) → DDNS。
  4. Cloudflare 用灰云,橘云代理不转 UDP,开了必连不上。
  5. 不用碰 nginx,UDP 游戏封包不经过反向代理。
  6. 世界设定写进 compose 的 environment,别手改 ini(会被重新生成盖掉)。
  7. 朋友版本不符 = server 落后;比对 buildid,重启容器触发 SteamCMD 更新,更新前先 RCON 广播 + 存档。
  8. 备份排除游戏本体(18GB),只单独备份存档目录。
參考連結
  • thijsvanloef/palworld-server-docker —— 这篇用的 Docker 映像GitHub
  • Palworld 官方专用伺服器说明Palworld Wiki
  • Cloudflare —— 为什么 Proxy(橘云)只走 HTTP/HTTPSCloudflare Docs