2026-08-08 12:31:04 +02:00
2026-08-08 12:31:04 +02:00
2026-08-08 11:56:08 +02:00
2026-08-08 12:31:04 +02:00
2026-08-08 12:31:04 +02:00
2026-08-08 12:31:04 +02:00

AI Disclosure

yeah, 100%. I read the code, should be fine.

Zomboid savefiles are a mess IMHO, this was the quickest way to get to the goal.

If you don't like it, don't use it, idk.


Zomboid Converter

client-hosted → dedicated server

Rust CLI that converts a locally hosted Project Zomboid multiplayer world into a dedicated server layout.

The world save format is the same (B42 Saves/Multiplayer/…). Conversion is packaging + naming + players.db world rename + mod recovery — not a binary rewrite.

This repository is only the converter. Sibling folders next to a checkout (e.g. original/, server/) are optional local testing aids and are not part of the git tree.

Build

cargo build --release
# binary: target/release/zomboid-converter

Copy the binary wherever you run the dedicated server (example below: ~/docker/zomboid/bin/).

Inputs / outputs

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. They are not inside the world folder. Pass the directory that contains those files with --server-config.

--input may be a world directory or a .zip (opened read-only; extracted to a temp dir). Paths ending in _player are rejected.

--output is the dedicated server-data root (writes Saves/Multiplayer/<server-name>/ and patches Server/<server-name>.ini).

Example (Hetzner / Docker layout)

Assumes:

Path Role
~/docker/zomboid/ Dedicated server (docker-compose.yml, .env)
~/docker/zomboid/server-data/ Zomboid userdir (Saves, Server, db, …)
~/docker/zomboid/server-files/ Game install (Steam download target)
~/docker/zomboid/bin/zomboid-converter This binary
~/docker/copyparty/data/zomboid/… Uploaded host world + configs
# stop the server first if it is running
cd ~/docker/zomboid
docker compose stop

./bin/zomboid-converter \
  --input ~/docker/copyparty/data/zomboid/alt/serverb42stable \
  --output ~/docker/zomboid/server-data \
  --server-name pzserver \
  --server-config ~/docker/copyparty/data/zomboid/alt \
  --force

docker compose up -d
docker compose logs -f

Or edit and run ~/docker/zomboid/convert-and-start.sh (config block at the top).

--server-name (default pzserver) must match SERVER_NAME in the server .env. It is only a basename for the dedicated world and Server/*.ini files.

--workshop-dir defaults to <output>/../server-files/steamapps/workshop/content/108600 when that directory exists (fills WorkshopItems=). Until Steam has downloaded mods once, re-run after the first install if needed.

Arguments

Flag Description
-i, --input Hosted world dir or zip
-o, --output Dedicated server-data root
-n, --server-name World + config basename (default: pzserver)
--server-config Dir with host <WorldName>.ini, _SandboxVars.lua, spawn files
--workshop-dir …/workshop/content/108600 — fills WorkshopItems= (auto if present next to server-data)
--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

  1. Copies the world into output/Saves/Multiplayer/<server-name>/
  2. Skips syncthing conflict sidecars (*-ZwS1g3qi*), *.db-journal, and player-cache junk
  3. Sets networkPlayers.world in players.db to <server-name>
  4. Recovers mod IDs from WorldDictionaryReadable.lua into Server/<name>.ini (Mods=)
  5. Resolves Steam workshop IDs into WorkshopItems= when a workshop dir is available
  6. Optionally sets Password= from --password or SERVER_PASSWORD / PASSWORD in .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 notes

Use any Project Zomboid dedicated image/layout you like. With Docker next to server-data:

  • SERVER_NAME must match converter --server-name
  • Optional: SERVER_PASSWORD in .env for the converters Password= patch
  • Also typical: ADMIN_USERNAME / ADMIN_PASSWORD, RCON_PASSWORD, memory, ports
  • Ports: 16261/udp, 16262/udp, 27015/tcp (RCON)
  • Stop the container before re-running the converter with --force
  • After first start, wait until workshop items finish installing (Workshop: … Installed in logs)
  1. Stop the dedicated server.
  2. Upload/copy the host world (not the _player folder) and ideally the host Server/<WorldName>.* configs.
  3. Run the converter with --server-config pointing at the config directory.
  4. Start the dedicated server; wait for workshop installs if needed.
  5. 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, dedicated defaults remain.
Mods with no WorldDictionary entries Pure Lua/QoL mods may not appear in recovered Mods=.
Workshop versions IDs come from the workshop cache on disk; newer mod builds than the host used can cause chunk LENGTH mismatch / blam errors.
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 the original host world if that happened.

Example passwords

# next to docker-compose (.env)
SERVER_PASSWORD=hunter2

# or CLI override
zomboid-converter … --password 'hunter2'
Description
No description provided
Readme 64 KiB
Languages
Rust 100%