友達がパルワールド(Palworld)の 1.0 を見て、一緒にやろうと誘ってきた。彼らは二年前に一度クリア済みで、この大型アップデートを機にもう一周したいらしい。私もちょうどその時期は暇だった。

正直、このゲームへの第一印象はよくなかった。当時の「ポケモンのパクリ」騒動があったからだ。でも後にその訴訟に勝ち、1.0 の更新幅も本当に大きいので、思ったほどひどくないのかもと、私も手を出した。

友達は最初、Radmin VPN でみんなを繋いでいた。この方法は駄目とは言わないが、私にはいつも資安面のもやもやが残る。Radmin のような仮想 LAN は P2P で動く。両端が直接繋げるなら直接繋ぎ、相手の NAT やファイアウォールを抜けられないと、サービス提供元自身のに退避する。友達の家のネットワークを考えると、どの組も全部抜けられるとは思えず、大半は中継に落ちるだろう。中継の代償は二段階ある。一つは遅い(パケットが遠回りする)、もう一つはゲームのトラフィックが無料・クローズドソースのサービスのサーバーを通ること。確かに全体は暗号化されている。だが接続を第三者に一周させるかどうかは、私にとっては不要なリスクだ。そして私にはちょうど 24/7 で動いている家用マシンがあったので、彼らの進行がまだ浅いうちに割り込んだ。専用サーバーを立てよう、みんな私の家に直接繋げばいい。

というわけでこの記事だ。Docker でパルワールド専用サーバーを立てるのは難しくない。docker compose up で数分で動きだす。難しいのは次の一歩:立ったのに、友達が入ってこられない。 ここで詰まる人は、「どう立てるか」で詰まる人よりずっと多い。この記事は全行程を一通り歩く。リソース見積もり、docker-compose、最も詰まりやすい UDP 接続、そして「友達がバージョン不一致で入れないと言う」の直し方まで。すべてのコマンドはそのまま実行でき、パスワードと IP はプレースホルダなので、自分のものに置き換えてほしい。

まず、どの image を使うか、そしてメモリは実際どれだけ食うか#

先に一言:多くの人が自ホストで使うのは公式の専用サーバーツール(SteamCMD で直接 PalServer を引く)で、必ずしも Docker ではない。 私が Docker を選ぶのは、単に使い慣れていて、マシン上の他サービスと一貫して管理しやすいからだ。あなたも常時起動のサーバーがあって 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 を落とさないこと。パルワールドは UDP で動く。うっかり TCP と書くと、サーバーは起動し、自分のローカルからも繋げるが、外の人は一人も入れない。
  • RCON を 127.0.0.1 に固定:自動バックアップ/再起動が先に警告をブロードキャストして優雅に終了するための管理チャネルで、ローカル専用、外に出してはいけない。
  • mem_limitAUTO_REBOOT_ENABLED は一組で、上のメモリ肥大に真正面から対処するもの。

本当に詰まるのはネットワーク:UDP 接続の三つの関門#

docker compose up -d のあと、サーバーは 8211 で動き、同じネットワークから LANの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:外部 → LAN、しかも 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 パケットは転送しない。パルワールドのドメインをオレンジの雲にすると、友達は 100% 繋がらず、しかも静かに失敗する(ウェブ系サービスは正常で、ゲームだけ繋がらない。ここが原因とは結びつけにくい)。必ず灰色の雲にして、ドメインを家の IP に直接解決させること。

二つのよくある誤解を、時間を無駄にする前にはっきりさせておく:

NOTE

nginx はいじらなくていい、一文字も ウェブサービスを立て慣れた人は、「外向け」と聞いた途端にリバースプロキシを思い浮かべる。だが 。パルワールドは UDP のゲームパケットで、「ルーター → ufw → コンテナ」を直通し、nginx とは完全に無関係だ。今回 /etc/nginx は一文字もいじらない。

では 27015 は開ける必要があるのか。これは で、「あなたのサーバーの状態が Steam のサーバーブラウザに表示されるかどうか」だけに関わる。最小限にしたいなら まず 8211 だけ開ける。友達は あなたのアドレス:8211 に直接繋いで遊べる。誰かが本当に Steam のお気に入りからオンライン状態を見たがったら、あとで 27015 を足せばいい。

ワールド設定の落とし穴:ini を直接いじると上書きされる#

サーバーが繋がったら、経験値倍率などを調整したくなるはずだ。ここに必ず踏む落とし穴がある:この 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拠点の作業パル上限20
SERVER_PLAYER_MAX_NUM人数上限32
PVP / ENABLE_FRIENDLY_FIREPvP / フレンドリーファイアtrue / false

書き換えたら docker compose up -d でコンテナを再構築し、新しい環境変数から ini を再生成すれば設定が効く。効いているか確かめるには、コンテナ内の生成された ini を見る(docker exec palworld cat .../PalWorldSettings.ini)。自分の値になっているのを確認してから終える。

友達が「バージョン不一致」で入れない:サーバーが遅れている#

数日遊んで、ある友達が「入れない、バージョン不一致と出る」と報告してきた。ほぼ必ず同じ原因だ:彼の 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 でバックアップする仕組みがあるなら、パルワールドを足したあとは要注意だ:あの data/ フォルダはゲーム本体まるごと、18GB 近くを抱えていて、放っておくと毎日バックアップディスクに詰め込まれる。

本当にバックアップが要るのはセーブだけで、あの 18GB のゲームファイルではない(ゲームファイルはいつでも再ダウンロードできる)。だから:

  • メインの rsync から palworld/data(ゲーム本体)を除外する
  • セーブディレクトリ(Pal/Saved)を単独でバックアップする。消えたら痛いのはそこだ
  • パルワールドのセーブも既存の古いバックアップ整理ループに入れる(例:7 日だけ残す)

.gitignore の方も data/ が除外されているかついでに確認し、18GB のゲームファイルがバージョン管理に紛れ込まないようにする。

一ページのチェックリスト#

全行程を、そのまま写せる順番に凝縮:

パルワールド自ホストチェックリスト
  1. RAM に怯えない:小規模なら実測 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 Wiki
  • Cloudflare —— なぜ Proxy(オレンジの雲)は HTTP/HTTPS しか通さないのかCloudflare Docs