5.0 KiB
Zomboid client-hosted → dedicated server
Convert a locally hosted Project Zomboid multiplayer world into a dedicated server layout, then run it with Docker.
The world save format is the same (B42 Saves/Multiplayer/…). Conversion is packaging + naming + players.db world rename + mod recovery — not a binary rewrite.
Layout
Paths below are relative to the repo root (parent of this converter/ folder):
| Path | Role |
|---|---|
original/ |
Source saves / zips (do not edit the zips) |
converter/ |
This crate — Rust CLI (zomboid-converter) |
server/ |
Docker dedicated server (docker-compose.yml, .env) |
server/server-data/ |
Zomboid userdir (Saves, Server, db, Logs) |
server/server-files/ |
Game install (Steam download target) |
Typical source pair from a host:
Saves/Multiplayer/<WorldName>/— authoritative world (use this)Saves/Multiplayer/<WorldName>_player/— client cache (ignore)
Server settings live separately under Zomboid/Server/<WorldName>.* on the host machine. They are not inside the world folder.
Prerequisites
- Rust toolchain (for the converter)
- Docker + Docker Compose
- Optional: local Steam workshop cache for app
108600(to resolveWorkshopItems)
Converter
cd converter
cargo build --release
cargo run --release -- \
--input ../original/serverb42stable \
--output ../server/server-data \
--server-name pzserver \
--workshop-dir /path/to/steamapps/workshop/content/108600 \
--force
--input may be a world directory or a .zip (opened read-only; extracted to a temp dir). Paths ending in _player are rejected.
Arguments
| Flag | Description |
|---|---|
-i, --input |
Hosted world dir or zip |
-o, --output |
Dedicated server-data root |
-n, --server-name |
World + config basename (default: pzserver) |
--workshop-dir |
…/workshop/content/108600 — fills WorkshopItems= |
--server-config |
Optional host Server/ dir to copy/rename configs |
--password |
Server join password (Password= in ini); overrides .env |
--env-file |
.env for password (default: <output>/../.env) |
--force |
Replace existing Saves/Multiplayer/<server-name>/ |
What it does
- Copies the world into
output/Saves/Multiplayer/<server-name>/ - Skips syncthing conflict sidecars (
*-ZwS1g3qi*),*.db-journal, and player-cache junk - Sets
networkPlayers.worldinplayers.dbto<server-name> - Recovers mod IDs from
WorldDictionaryReadable.luaintoServer/<name>.ini(Mods=) - If
--workshop-diris set, resolves Steam workshop IDs (WorkshopItems=) - Optionally sets
Password=from--passwordorSERVER_PASSWORD/PASSWORDin.env
It does not overwrite db/<server-name>.db (accounts / whitelist stay managed by the server).
Characters / login
Characters bind to the account username in players.db (networkPlayers.username), not Steam display name. Players must log in with that exact username (or you rename the column before they join).
Dedicated server (Docker)
cd ../server
cp .env.example .env # if needed; set passwords / SERVER_NAME
docker compose up -d
docker compose logs -f
Important .env keys:
SERVER_NAME— must match converter--server-name(defaultpzserver)ADMIN_USERNAME/ADMIN_PASSWORD— admin accountRCON_PASSWORD— RCONSERVER_PASSWORD— optional join password for the converter (Password=in ini)
Ports: 16261/udp, 16262/udp, 27015/tcp.
Stop the container before re-running the converter with --force.
Recommended migration flow
- Stop the dedicated server.
- Copy the host world into
original/(keep zips untouched as backups). - Ideally also copy the host’s
Zomboid/Server/<WorldName>.ini(+_SandboxVars.lua, spawn files) and pass--server-config. - Run the converter with
--workshop-dirand--force. - Start Docker; wait until workshop items finish installing (
Workshop: … Installedin logs). - Join with the same username as in the hosted
players.db.
Known gaps / caveats
| Item | Notes |
|---|---|
Host SandboxVars / spawn / Map= |
Not in the world save. Without --server-config, the dedicated defaults remain. |
| Mods with no WorldDictionary entries | Pure Lua/QoL mods may not appear in recovered Mods=. |
| Workshop versions | IDs are resolved from your local workshop cache; newer mod builds than the host used can cause chunk LENGTH mismatch / blam errors and odd world state (e.g. zombies in cleared bases). |
ZombieMigrate |
Dedicated sandbox may allow migration even when respawn is off; align sandbox with the host when possible. |
| Running without mods first | Can rewrite WorldDictionary and map chunks. Re-import from original/ if that happened. |
Example passwords
# ../server/.env (next to docker-compose)
SERVER_PASSWORD=hunter2
# or CLI override (from this directory)
cargo run --release -- … --password 'hunter2'