Project Zomboid Server in Docker Compose (BETA)

Climbing up from ocean
Photo by Daniel Jensen / Unsplash

Usually I don't like to expose infrastructure information but this time I make an exception. Some parts of the code might be changed for privacy reasons though.

Before focusing on IT-Security related content I wanted to create a Project Zomboid Gaming Server during xmas holiday season.

The code is free to use.

Docker compose setup

I started using the steamcmd:root image from cm2network: https://hub.docker.com/r/cm2network/steamcmd/

The docker image itself only provides steamcmd which then can be used to host different kind of Steam dedicated servers.

I opted for the root version of the image for developing purposes such as installing Linux packages during runtime.

# docker-compose.yml 
services:
  steamcmd:
    image: cm2network/steamcmd:root
    container_name: steamcmd
    tty: true
    ports:
      - "0.0.0.0:16261:16261/udp"
      - "0.0.0.0:16262:16262/udp"
    volumes:
     - /mount/steamcmd_login_volume:/home/steam
     - /mount/steamcmd_volume:/home/steam/steamcmd

Beside defining the steamcmd:root image I also had to open both ports udp/16261 and udp/16262 used by the game client.

The volumes themselves are (persistent) bind mounts.

Installation

The initial installation procedure can be found on the PZWIKI page. I followed the installation procedure for steamcmd using the steam user:

cat >$HOME/update_zomboid.txt <<'EOL'
// update_zomboid.txt
//
@ShutdownOnFailedCommand 1 //set to 0 if updating multiple servers at once
@NoPromptForPassword 1
force_install_dir /opt/pzserver/
//for servers which don't need a login
login anonymous 
app_update 380870 validate
quit
EOL
steamcmd +runscript $HOME/update_zomboid.txt

Source: https://pzwiki.net/wiki/Dedicated_Server#Linux

Auto Update

The script below uses three functions to keep the Project Zomboid Server up2date from outside of the steamcmd docker container. The script is stored under " /etc/cron.hourly".

"checkupdate()" is used to get the installed build version number as well as the latest build number from https://api.steamcmd.net.  In case the Project Zomboid Server is up2date a log entry gets created and the script exits.

"updater()"  gets activated once there is a new build avaibable. It first kills the game server before executing the update. Afterwards the server gets started again in background. Consider using a better way to close the server than just terminating the Linux process. I am just lazy :)

"check_availability()" then check if the processes are running and report the status accordingly to the logs.

Replace <SERVERNAME> with the servername set during the initial setup.

#!/bin/bash

checkupdate() {
        # get current build id
        CURRENTBUILD=$(docker exec steamcmd grep -i 'buildid' /home/steam/Steam/logs/content_log.txt | tail -n 1 | sed -n 's/^.*BuildID\s\([0-9]\+\).*$/\1/p')

        # get latest build id from steamcmd API
        LATESTBUILD=$(curl -s https://api.steamcmd.net/v1/info/380870 | jq '.["data"]["380870"]["depots"]["branches"]["public"]["buildid"]' | cut -d "\"" -f 2)

        if [ $CURRENTBUILD == $LATESTBUILD ]; then
                logger -p Info -t ZomboidUpdater INFO: "PZserver up2date. Exiting.."
                exit
        else
                :
        fi
}

updater() {
        #0 install ps just in case the container got rebooted
        docker exec steamcmd apt update
        docker exec steamcmd apt install procps -y

        #1 kill pzserver
        for i in $(docker exec steamcmd ps -aux | grep '<SERVERNAME>' | awk '{print $2}'); do docker exec steamcmd kill $i; done

        #2 update pzserver
        docker exec -u steam steamcmd /home/steam/steamcmd/steamcmd.sh +runscript /home/steam/Steam/servers/projectzomboid/update_zomboid.txt

        #3 start pzserver
        docker exec -u steam steamcmd nohup /home/steam/Steam/servers/projectzomboid/start-server.sh -servername <SERVERNAME> &
}

check_availability() {
        # check if pzserver proccesses are running and assign it to the PSTAT variable
        PSTAT=$(docker exec steamcmd ps -aux | grep '<SERVERNAME>' | wc -l)

        # if the NTSTAT variable equals "2" then both of the pzsever ports are open and everything runs as expected
        if [ $PSTAT  == 2 ]; then
                logger -p Info -t ZomboidUpdater SUCCESS: "Processes are UP. PZserver seems to run"
        else
                logger -p Error -t ZomboidUpdater FAILED: "Processes are DOWN. PZserver does not seem to run"
        fi
}

checkupdate
updater
check_availability