VPS Tutorials
Set Up a Project Zomboid Dedicated Server on a VPS (2026): Build 42, Ports, Configuration, Backups and Updates
Build a Project Zomboid Build 42 dedicated server from scratch on an Ubuntu VPS with SteamCMD, covering App ID 380870, 16261/16262 UDP, configuration, admins, whitelist, world backup and restore, and automatic updates.

If you want to run a zombie apocalypse world with friends over the long term and don't want the host to have to be online every time play continues, a Project Zomboid dedicated server is the more stable option. The world, character accounts, sandbox parameters, whitelist, mods and backups all live on the VPS, and teammates can join on their own schedule.
This article uses Ubuntu 24.04 LTS, SteamCMD, the current Build 42 stable branch and systemd as examples to complete server installation, 16261/16262 UDP, firewall, server configuration, admins, whitelist, world backup, restore and automatic updates from scratch. The main text does not use game images of unknown origin, and does not apply old Build 41 or early Build 42 Unstable tutorials directly to the current stable version.
As of August 2026, Project Zomboid has officially pushed Build 42.20 to the Steam stable branch and released the 42.20.1 hotfix. Therefore, a new server can use the Steam default stable branch, and there is no need to add -beta unstable, b42multiplayer or Build 41 parameters from old tutorials.
This point is very important:
- The game client and the dedicated server must be on compatible versions.
NONEin Steam properties means the current stable branch.- Before upgrading from Build 41 to Build 42, you must back up; do not assume old worlds, mods and configurations will migrate without loss.
- Official security updates and multiplayer fixes change server files, so keeping updates disabled for a long time is not safe.
All commands in this article target the current stable branch. When the official team releases a new stable version in the future, read the patch notes, back up the world, and then update during a maintenance window.
Project Zomboid has not published a set of VPS minimum specifications that guarantees any map, mod count and player count. Players exploring separately, driving to quickly load new chunks, large numbers of zombies, map mods and Workshop content all increase CPU, memory, disk and network pressure. The following are practical starting points for purchasing and stress testing, not official performance promises:
| Usage scale | Suggested VPS starting point | Disk | Suitable scenarios |
|---|---|---|---|
| 2–4 player vanilla server | 4 high-performance vCPUs, 6–8GB memory | 50GB NVMe | Private whitelist, survival save |
| 5–10 player long-term server | 4–6 high-performance vCPUs, 10–12GB memory | 80GB NVMe | Multiplayer exploration, vehicles, long-term map |
| 10+ players or modded server | 6–8 cores, 16GB+ memory | 120GB+ NVMe | Map mods, higher zombie counts, public server |
Prefer nodes with stable single-core performance, low NVMe latency and good UDP quality. Do not look only at the number of vCPUs; a heavily oversold 8-core may be worse than a stable 4-core. The data center should be close to the main players, and confirm that the provider allows game servers, provides sufficient traffic and has a clear DDoS handling policy. Before purchasing, you can refer to Game server VPS configuration and network line selection.
| Method | Advantages | Cost | Who it suits |
|---|---|---|---|
| SteamCMD + systemd | Transparent paths, low resource overhead, direct logs | Mainly managed via SSH | Single VPS, one world |
| In-game Host | Simple configuration, suitable for temporary co-op | Host must be online, not a true 24-hour service | Small team temporary session |
| Pterodactyl | Web panel, users, quotas and multiple instances | Extra maintenance of Panel, Wings, database | Multiple worlds or multi-person operations |
This article uses SteamCMD + systemd. If you plan to host Minecraft, Valheim, CS2 and Project Zomboid on the same machine, consider the Pterodactyl game server panel deployment guide.
The current regular Steam dedicated server should first be deployed with the minimum port set:
| Port | Protocol | Purpose | Public |
|---|---|---|---|
| 16261 | UDP | Main connection and Steam Relay | Yes |
| 16262 | UDP | Direct connection | Yes |
| 22 | TCP | SSH management | Admin IP only, or use the actual SSH port |
| 27015 | TCP | RCON, only needed if enabled | Not open by default |
Project Zomboid official support staff clearly stated in port troubleshooting that the current game only needs 16261 and 16262 UDP open; content online requiring a range of TCP ports for each player mostly comes from older versions. This article does not enable RCON, so 27015 is not opened.
The server configuration name uses survival. It determines the configuration file and world directory names, for example:
survival.ini
survival_SandboxVars.lua
Zomboid/Saves/Multiplayer/survival
Use only lowercase letters, numbers and hyphens in the configuration name to avoid spaces and special characters causing confusion in scripts, backups or service names.
First update the system and install the basic components required by SteamCMD:
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl ca-certificates tar lib32gcc-s1 ufw
Create a service account that cannot log in directly with a password and a fixed directory:
sudo adduser --disabled-password --gecos "" pz
sudo install -d -o pz -g pz /opt/steamcmd
sudo install -d -o pz -g pz /opt/pzserver
sudo install -d -m 750 -o root -g pz /var/backups/project-zomboid
Do not run the Java game service as root. A dedicated user limits process permissions and also avoids root-owned files after a SteamCMD update, which would cause the next update to fail.
Switch to the pz user and download Valve's official Linux SteamCMD:
sudo -iu pz
cd /opt/steamcmd
curl -fsSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz -o steamcmd_linux.tar.gz
tar -xzf steamcmd_linux.tar.gz
./steamcmd.sh +quit
Confirm the entry file exists:
test -x /opt/steamcmd/steamcmd.sh
The Steam App ID for Project Zomboid Dedicated Server is 380870. Anonymous login is enough to download the server:
/opt/steamcmd/steamcmd.sh \
+force_install_dir /opt/pzserver \
+login anonymous \
+app_update 380870 validate \
+quit
The command has no -beta parameter, so it gets the current stable branch. Use validate for the first installation or to repair corrupted files; daily updates usually do not need to re-validate all files every time.
Check the startup script and installation size:
test -x /opt/pzserver/start-server.sh
grep -nE -- '-Xm[sx]' /opt/pzserver/start-server.sh
du -sh /opt/pzserver
If you see other tutorials still requiring downloading Build 41 or adding the Unstable beta parameter for Build 42, check the publication date first. Build 42.20 has entered the default stable branch, and copying old branch parameters will cause client and server version mismatch.
The default Java heap in the startup script may not suit your VPS. The following uses an 8GB memory machine as an example, setting the initial heap to 2GB and the maximum heap to 6GB:
cp /opt/pzserver/start-server.sh /opt/pzserver/start-server.sh.stock
sed -Ei 's/-Xms[0-9]+[gGmM]/-Xms2g/; s/-Xmx[0-9]+[gGmM]/-Xmx6g/' \
/opt/pzserver/start-server.sh
grep -nE -- '-Xm[sx]' /opt/pzserver/start-server.sh
The final output must contain -Xms2g and -Xmx6g. Do not set -Xmx to all of the VPS memory; Ubuntu, file cache, SteamCMD and backup compression still need space. A 10–12GB VPS can start from -Xms2g -Xmx8g, a 16GB VPS can start from -Xms4g -Xmx12g, and then adjust according to actual peaks.
SteamCMD updates may overwrite start-server.sh, so re-check the heap parameters after every update. The update script later will automatically reset and verify them.
The first run must be completed in an interactive terminal, because the server will ask you to create an admin account password:
cd /opt/pzserver
./start-server.sh -servername survival
Enter a strong password used only for the Project Zomboid admin as prompted. Do not reuse it with SSH, Steam or other websites. Wait for the log to show:
*** SERVER STARTED ****
Then enter in the server console:
quit
Wait for the process to save the world and exit normally. Do not close SSH directly or send a forced termination signal. The first run generates configuration, database, logs and world directories under /home/pz/Zomboid.
Exit the service account:
exit
Open the main configuration:
sudo -u pz nano /home/pz/Zomboid/Server/survival.ini
Find and adjust the following items in the existing file; do not overwrite the file automatically generated by Build 42 with a complete old-version configuration:
Public=true
PublicName=My Project Zomboid Server
PublicDescription=Private Build 42 survival server
Password=REPLACE_WITH_PLAYER_PASSWORD
DefaultPort=16261
UDPPort=16262
MaxPlayers=8
PauseEmpty=true
Open=true
AutoCreateUserInWhiteList=false
DoLuaChecksum=true
SteamVAC=true
RCONPassword=
These settings achieve: public server list, 8-player limit, pause when empty, create account on first login, Lua checksum and Steam VAC. Keep RCONPassword empty; this article does not enable the remote control port.
If you want a strict whitelist, set Open=false, then have the admin create player accounts or add them to the whitelist. Do not think that setting only a server public password means there is a whitelist; the two control different layers.
Generate a separate player password:
openssl rand -base64 24
Write the result into Password. The configuration file is owned by the pz user:
sudo chown pz:pz /home/pz/Zomboid/Server/survival.ini
sudo chmod 600 /home/pz/Zomboid/Server/survival.ini
Build 42 world rules are located at:
/home/pz/Zomboid/Server/survival_SandboxVars.lua
Back up the automatically generated file first:
sudo -u pz cp /home/pz/Zomboid/Server/survival_SandboxVars.lua \
/home/pz/Zomboid/Server/survival_SandboxVars.lua.initial
sudo -u pz nano /home/pz/Zomboid/Server/survival_SandboxVars.lua
Sandbox fields change with version updates, so it is not recommended to copy an entire Lua file from an old Build 41 tutorial. The safest method is to create a template in the Host settings interface of a same-version client, then compare the generated *_SandboxVars.lua and migrate only the fields you understand.
Back up the world before modifying zombie counts, loot, vehicles, infection or respawn rules. Some rules only fully take effect for new areas or new worlds; do not treat "the file saved successfully" as proof that the save has migrated without loss.
Create the unit:
sudo nano /etc/systemd/system/project-zomboid.service
Write:
[Unit]
Description=Project Zomboid Dedicated Server
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=pz
Group=pz
WorkingDirectory=/opt/pzserver
ExecStart=/opt/pzserver/start-server.sh -servername survival
Restart=on-failure
RestartSec=15
KillSignal=SIGTERM
TimeoutStopSec=180
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
Load, start and check status:
sudo systemctl daemon-reload
sudo systemctl enable --now project-zomboid
sudo systemctl status project-zomboid --no-pager
sudo journalctl -u project-zomboid -n 200 --no-pager
The log should show *** SERVER STARTED **** and information that Steam is enabled. The first foreground initialization has already created the admin, so systemd will not get stuck at the password prompt.
After stopping the service, wait for the Java process to save and exit:
sudo systemctl stop project-zomboid
sudo systemctl status project-zomboid --no-pager
If it still has not exited after 180 seconds, check the logs and disk space first; do not immediately use kill -9. Forced termination may corrupt chunks or player data being written.
Keep the current SSH session open, allow SSH first, then open the two game ports:
sudo ufw allow OpenSSH
sudo ufw allow 16261/udp
sudo ufw allow 16262/udp
sudo ufw status numbered
sudo ufw enable
The cloud provider security group must also add the same UDP rules. Check service listening:
sudo ss -lunp | grep -E ':16261|:16262'
Do not open 16261–16300 for all protocols just for convenience, and do not copy the old "one TCP port per player" rules. The current official support reply defines 16261 as the main connection/Steam Relay and 16262 as direct connection.
If you use pz.example.com, create an A record pointing to the VPS IPv4. A normal Cloudflare orange-cloud HTTP proxy cannot forward game UDP; set it to DNS only, or have players connect directly to the public IP.
In the Project Zomboid main menu, select Join and add a favorite:
IP: 203.0.113.20
Port: 16261
Server Password: 你在 survival.ini 设置的 Password
Account Username: 玩家自己的服务器用户名
Account Password: 玩家自己的服务器账号密码
When testing over the public internet, replace the example IP with the VPS's real address. The server password and the player account password are not the same field; players should use separate usernames and passwords, and should not share the admin account.
When the connection fails, check both:
sudo systemctl is-active project-zomboid
sudo journalctl -u project-zomboid -n 250 --no-pager
sudo ss -lunp | grep -E ':16261|:16262'
sudo ufw status numbered
The admin account created on first startup is only for management, not for daily play. After logging in with admin on the client, you can manage players and the server in the in-game admin panel.
When you need to give another player admin rights, run the game admin command:
/setaccesslevel "username" admin
Revoke rights:
/setaccesslevel "username" none
When enabling a strict whitelist, keep Open=false, and create or allow specific accounts through the admin interface. Check regularly:
sudo ls -lh /home/pz/Zomboid/db
sudo ls -lh /home/pz/Zomboid/Server
The player account and permission database is also part of the backup scope. Do not back up only the map directory, otherwise the whitelist, admins or player login information may be lost after restore.
This article uses the survival configuration name; key data is usually located at:
/home/pz/Zomboid/Server/
/home/pz/Zomboid/Saves/Multiplayer/survival/
/home/pz/Zomboid/db/
/home/pz/Zomboid/Logs/
Confirm with actual files; do not rely only on tutorial paths:
sudo -u pz find /home/pz/Zomboid -maxdepth 3 -type f | sort | head -n 120
sudo du -sh /home/pz/Zomboid
/opt/pzserver are program files that can be re-downloaded by SteamCMD; /home/pz/Zomboid is the world, configuration, account database and log data that must be protected.
During a maintenance window, stop the server and package the entire data directory:
sudo systemctl stop project-zomboid
sudo tar -C /home/pz -czf \
"/var/backups/project-zomboid/pz-data-$(date -u +%Y%m%dT%H%M%SZ).tar.gz" \
Zomboid
sudo systemctl start project-zomboid
sudo systemctl is-active project-zomboid
Check the archive:
sudo ls -lh /var/backups/project-zomboid
sudo tar -tzf /var/backups/project-zomboid/pz-data-YYYYMMDDTHHMMSSZ.tar.gz | head -n 100
Replace the example filename with the actual archive. Sync at least one copy to another machine or object storage, and set a retention period. For a complete off-site plan, refer to the VPS backup and restore drill guide.
Before restoring, stop the service and keep the current directory:
sudo systemctl stop project-zomboid
sudo tar -tzf /var/backups/project-zomboid/pz-data-YYYYMMDDTHHMMSSZ.tar.gz | head -n 100
sudo mv /home/pz/Zomboid \
"/home/pz/Zomboid.before-restore-$(date -u +%Y%m%dT%H%M%SZ)"
sudo tar -C /home/pz -xzf \
/var/backups/project-zomboid/pz-data-YYYYMMDDTHHMMSSZ.tar.gz
sudo chown -R pz:pz /home/pz/Zomboid
sudo systemctl start project-zomboid
Verify the service, map and accounts:
sudo systemctl status project-zomboid --no-pager
sudo journalctl -u project-zomboid -n 250 --no-pager
sudo du -sh /home/pz/Zomboid/Saves/Multiplayer/survival
Log in with a test player and confirm that spawn points, characters, vehicles, containers, whitelist and admin permissions are all normal. Keep Zomboid.before-restore-* before acceptance; do not immediately delete the only rollback copy.
Back up the world and stop the service first, then update App ID 380870. Create the script:
sudo nano /usr/local/sbin/update-project-zomboid-server
Write:
#!/usr/bin/env bash
set -euo pipefail
backup_dir=/var/backups/project-zomboid
stamp=$(date -u +%Y%m%dT%H%M%SZ)
was_active=0
install -d -m 750 -o root -g pz "$backup_dir"
if systemctl is-active --quiet project-zomboid; then
was_active=1
systemctl stop project-zomboid
fi
restart_service() {
if [ "$was_active" -eq 1 ]; then
systemctl start project-zomboid
fi
}
trap restart_service EXIT
tar -C /home/pz -czf \
"$backup_dir/pz-before-update-$stamp.tar.gz" \
Zomboid
sudo -u pz /opt/steamcmd/steamcmd.sh \
+force_install_dir /opt/pzserver \
+login anonymous \
+app_update 380870 \
+quit
sed -Ei 's/-Xms[0-9]+[gGmM]/-Xms2g/; s/-Xmx[0-9]+[gGmM]/-Xmx6g/' \
/opt/pzserver/start-server.sh
grep -q -- '-Xms2g' /opt/pzserver/start-server.sh
grep -q -- '-Xmx6g' /opt/pzserver/start-server.sh
chown pz:pz /opt/pzserver/start-server.sh
Set permissions and test manually:
sudo chown root:root /usr/local/sbin/update-project-zomboid-server
sudo chmod 750 /usr/local/sbin/update-project-zomboid-server
sudo /usr/local/sbin/update-project-zomboid-server
sudo systemctl is-active project-zomboid
sudo journalctl -u project-zomboid -n 200 --no-pager
The script does not use validate, reducing daily update time; validate manually when file corruption is found. If SteamCMD or heap parameter validation fails, the script exits with a non-zero status and attempts to restore the running state before the update.
Project Zomboid updates may change world or mod compatibility. A private vanilla server can update automatically in a fixed low-peak window; public servers and modded servers are better suited to manual execution after notifying players.
Create the service:
sudo nano /etc/systemd/system/project-zomboid-update.service
[Unit]
Description=Update Project Zomboid Dedicated Server
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/update-project-zomboid-server
Create the timer:
sudo nano /etc/systemd/system/project-zomboid-update.timer
[Unit]
Description=Project Zomboid daily maintenance window
[Timer]
OnCalendar=*-*-* 05:50:00
RandomizedDelaySec=10m
Persistent=true
[Install]
WantedBy=timers.target
Load and check the schedule:
sudo systemctl daemon-reload
sudo systemctl enable --now project-zomboid-update.timer
systemctl list-timers project-zomboid-update.timer
Do not force an update while players are online, vehicles are driving at high speed or the world is saving. Large stable version switches should disable the timer and test on a backup copy first.
Do not trial-run directly on the only Build 41 save. The safe process is:
- Stop the old server and back up the entire
Zomboiddirectory. - Save the old server version, configuration and mod list.
- Copy the save in a separate directory and test the conversion with the current stable version.
- Temporarily disable all mods and confirm the vanilla world can load.
- Check characters, vehicles, containers, map chunks and admin accounts.
- Only after complete acceptance should the production server switch.
During Build 42 development, the official team repeatedly warned that version updates may break old saves, and recommended new saves, no-mod reproduction and backing up before updating. Even though Build 42 has entered the stable version, it does not mean all Build 41 mods or old saves are necessarily compatible.
If you want to keep a long-term world, backup and restore capability is more important than "whether it can convert successfully in one go". You can refer to the Valheim dedicated server world backup process to establish the same habits of stopping the service, archiving, off-machine copying and restore acceptance.
Let the vanilla server run stably first, then add mods. The official team issued a security notice in 2026 related to malicious Workshop content and continues to patch Build 41/42 security issues; therefore Workshop subscription counts cannot be treated as a security audit.
Before adding mods:
- Check whether the mod explicitly supports the current Build 42 stable version.
- Record the order and versions of
WorkshopItemsandMods. - Check the author, update history, comments and external download requirements.
- Do not execute scripts or binaries of unknown origin required by mods.
- Test on a backup copy; do not directly contaminate the production world.
- After updating, first reproduce crashes with a no-mod world, then determine whether it is a game or mod issue.
Do not let an unattended timer automatically cross major version updates. The update window for a modded server should include backup, mod compatibility checks and rollback time.
Record data during player peaks, driving through new chunks and world saving:
free -h
df -h /opt/pzserver /home/pz/Zomboid /var/backups/project-zomboid
uptime
ps -eo pid,%cpu,%mem,rss,etime,cmd | grep '[z]ombie.network.GameServer'
sudo journalctl -u project-zomboid --since "30 minutes ago" --no-pager
Focus on:
- Single-core saturation when driving into new chunks: switch to a node with stronger single-core performance and lower disk latency.
- Memory rising quickly after players spread out: reduce player count, map scope or mod count, then evaluate scaling up.
- Stutter or rollback during saving: check NVMe latency, free space and forced termination records.
- Obvious latency during evening peak: check UDP packet loss, data center network lines and neighbor overselling.
- Logs or backups filling the root partition: set a retention period and move off-site copies off the local machine.
Do not mask mod memory leaks by endlessly increasing -Xmx. The official 42.20.1 hotfix includes multiplayer chunk unloading and memory usage fixes; staying on the current secure stable version is itself part of performance maintenance.
This means you skipped the first foreground initialization. Stop systemd and run in the foreground as the pz user:
sudo systemctl stop project-zomboid
sudo -iu pz
cd /opt/pzserver
./start-server.sh -servername survival
Set the admin password, wait for SERVER STARTED, enter quit, exit the account, and then start systemd.
Check the server installation manifest and update the stable branch:
grep -n 'buildid' /opt/pzserver/steamapps/appmanifest_380870.acf
sudo /usr/local/sbin/update-project-zomboid-server
sudo journalctl -u project-zomboid -n 200 --no-pager
Confirm the client Betas selection is NONE. Do not let the client use Build 41 or old Unstable while the server uses the current stable version; also do not solve version mismatch by deleting the world.
Check the configuration, listening and security group in order:
sudo grep -E '^(Public|PublicName|DefaultPort|UDPPort)=' \
/home/pz/Zomboid/Server/survival.ini
sudo ss -lunp | grep -E ':16261|:16262'
sudo ufw status numbered
sudo journalctl -u project-zomboid -n 250 --no-pager
Join directly using the public IP and 16261 to rule out list refresh issues. Confirm that both UFW and the cloud security group allow UDP 16261/16262, and that the domain does not have a normal HTTP proxy enabled.
This is usually a 16261/16262 UDP, cloud security group, version or Steam status issue. First test with a no-mod client, then check the logs for Steam, RakNet and connection errors:
sudo journalctl -u project-zomboid -n 300 --no-pager | \
grep -iE 'steam|raknet|connect|error|failed'
Do not open all ports 50000–65000 without basis. The official support reply states that the game itself only needs 16261 and 16262; additional high-port requirements are more likely from host provider, NAT or firewall configuration anomalies.
SteamCMD may overwrite the startup script. Check the heap parameters:
grep -nE -- '-Xm[sx]' /opt/pzserver/start-server.sh
free -h
sudo journalctl -u project-zomboid -n 250 --no-pager | grep -iE 'memory|heap|oom|killed'
Re-set -Xms/-Xmx suitable for the VPS, and confirm that both grep -q validations in the update script pass. Do not set the maximum heap to all physical memory.
First restore the pre-mod backup, temporarily clear WorkshopItems and Mods in the configuration, and reproduce with the vanilla server:
sudo systemctl stop project-zomboid
sudo cp /home/pz/Zomboid/Server/survival.ini \
/home/pz/Zomboid/Server/survival.ini.failed-mods
sudo -u pz nano /home/pz/Zomboid/Server/survival.ini
sudo systemctl start project-zomboid
sudo journalctl -u project-zomboid -n 300 --no-pager
After confirming vanilla is normal, restore mods in batches. Enabling dozens of mods at once makes conflicts, malicious content and version errors impossible to locate.
- SteamCMD installed App ID 380870 without adding old Unstable beta parameters.
- Both client and server use the current Build 42 stable branch.
- The service runs as the dedicated
pzuser, not root. - The first foreground start has created the admin, and systemd will not wait for interactive input.
-
systemctl is-active project-zomboidreturnsactive. - The log shows
*** SERVER STARTED ****and Steam enabled information. - 16261/16262 UDP are allowed in both UFW and the cloud security group.
- No unused RCON or large ranges of high ports are open.
-
survival.iniand the sandbox Lua come from files automatically generated by the current version. - Java heap parameters suit the VPS and will be re-validated after updates.
- The admin account is not used for daily play, and the public server has a whitelist policy configured.
- World, configuration, account database and logs are backed up together.
- A restore has been completed and characters, vehicles, containers and permissions verified.
- Build 42 compatible mods are added only after vanilla server acceptance.
- Before major updates, the timer is paused, players are notified and a rollback copy is kept.
Running Project Zomboid Build 42 on a VPS is not about copying a single SteamCMD command; it is about keeping the client and App ID 380870 on the same stable branch, correctly opening 16261/16262 UDP, completing the interactive admin initialization first, and then handing it over to systemd for persistent operation.
What really needs protection is the world, configuration, player database and permissions under /home/pz/Zomboid. Stopping the server and backing up before every update, re-validating Java heap parameters, and first reproducing problems with a no-mod world can significantly reduce the rollback risk caused by version migration, Workshop content and forced termination.