Hosting game servers using Docker on a VPS makes the setup clean, consistent, and easy to manage. In this guide, we use a fresh Ubuntu 24.04 or Ubuntu 22.04 and turn it into a powerful game server host for real servers, including Minecraft, Steam or Source-based games, Valheim, and Palworld.
We know that configuring a VPS can be complex. If you want a pre-configured VPS, PerLod Hosting offers game-optimized VPS plans with this entire stack pre-configured.
Requirements for Hosting Game Servers Using Docker on a VPS
We want to set up a few details to prepare our Ubuntu server for hosting game servers, including VPS firewall, Docker networking, and filesystem layout.
Tip: While this guide will walk you through the entire process, if you'd prefer a pre-optimized, game-ready VPS with this exact stack already configured, check out our high-performance VPS Hosting Plans.
Prepare the VPS for Hosting Game Servers
We start by preparing the VPS by installing essential tools, configuring the firewall, and setting up the correct timezone. This setup keeps your VPS secure and ready for Docker and game servers.
Run the system update and upgrade with the following command:
sudo apt update && sudo apt upgrade -y
Install the required tools on your server with the command below:
sudo apt install curl wget htop git jq ca-certificates gnupg ufw -y
Set the correct timezone on your server and verify it with the commands below:
sudo timedatectl set-timezone Asia/Dubaisudo timedatectl status
To configure UFW firewall rules, we only allow SSH now and configure game ports later:
sudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw allow OpenSSH
Enable UFW firewall and check its status with the commands below:
sudo ufw enablesudo ufw status
Install Docker Engine and Compose Plugin
At this point, you must install Docker and Docker Compose. Docker will containerize each game server, which keeps them isolated and easy to manage, and Docker Compose allows defining multi-container servers in a single file.
Add Docker's official repository and install it with the following commands:
sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg \| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \https://download.docker.com/linux/ubuntu $(. /etc/os-release; echo $UBUNTU_CODENAME) stable" \| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt updatesudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
Add user to Docker group to run Docker commands:
sudo usermod -aG docker $SUDO_USER
Remember to log out and then log in to apply the changes.
Verify your Docker and Docker Compose installation by checking their versions:
docker versiondocker compose version
Create Filesystem Layout for Game Servers
Now you must create the directory structure and common variables to organize files and persistent data so servers can be updated and restarted safely.
Create the directories with the following command:
sudo mkdir -p /opt/game-servers/{minecraft,steam-generic,backups}
You can also create an env file for defaults, which is optional, with the command below:
cat >/opt/game-servers/.env <<'EOF'TZ=Asia/DubaiPUID=1000PGID=1000EOF
The .env simplifies environment variables for all servers.
Note: To get your real user UID/GID, you can use the id command and replace it in the above file.
Choose Docker Networking Mode: Bridge vs Host
Networking mode affects latency, port mapping, and server connectivity. Here are the two modes for Docker networking:
- Bridge mode: Default, NATed, requires clear ports, safe for multiple containers.
- Host mode: Uses the VPS network directly, reducing latency for some games.
You can start with bridge mode, which maps only the ports you need, and try host mode if your Steam or Source server has connectivity or latency issues.
Create a shared bridge network for container DNS with the command below:
docker network create games-net || true
This network ensures servers can talk to each other if needed.
Deploy Minecraft Java Game Server
Minecraft is the most popular Java server. To deploy the game server, we use the itzg/minecraft-server image for easy setup.
Create a docker-compose.yml in /opt/game-servers/minecraft/ with the following command:
1cat >/opt/game-servers/minecraft/docker-compose.yml <<'YAML'2services:3 mc:4 image: itzg/minecraft-server:latest5 container_name: mc6 restart: unless-stopped7 networks:8 - games-net9 ports:10 - "25565:25565/tcp"11 environment:12 EULA: "TRUE"13 TZ: ${TZ:-UTC}14 15 TYPE: PAPER16 17 VERSION: "1.21.1"18 19 MEMORY: "4G"20 21 ONLINE_MODE: "TRUE"22 23 MOTD: "Dockerized Minecraft Server"24 ENABLE_COMMAND_BLOCK: "true"25 volumes:26 - ./data:/data27 - /etc/localtime:/etc/localtime:ro28 healthcheck:29 test: ["CMD", "mc-monitor", "status", "--host", "localhost"]30 interval: 30s31 timeout: 10s32 retries: 633 34networks:35 games-net:36 external: true37YAML
Open firewall port for it with the command below:
Navigate to the Minecraft game server directory:
cd /opt/game-servers/minecraft
Export the env file and run the Minecraft game server with the commands below:
set -a; . ../.env 2>/dev/null || true; set +adocker compose up -d
You can check logs using this command:
Everything is persisted in /opt/game-servers/minecraft/data.
Deploy Generic Steam or Source Game Server Template
This template uses SteamCMD to install or update a server on container start and then launch it. You can reuse it for CS2, Valheim, ARK, and others by setting environment variables. You will need the following factors:
- APP_ID: Find it on SteamDB or official docs.
- APP_DIR: Folder name under /home/steam/serverfiles.
- START_CMD: The game’s launch line, including ports.
- Ports to expose: TCP and UDP as required by the game.
Create a minimal Dockerfile with a non-root user with the following command:
cat >/opt/game-servers/steam-generic/Dockerfile <<'DOCKER'FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive \ STEAMCMDDIR=/opt/steamcmd \ SERVERDIR=/home/steam/serverfiles \ USER=steam HOME=/home/steam RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates locales tzdata curl wget file bzip2 xz-utils \ lib32gcc-s1 lib32stdc++6 libc6-i386 lib32z1 \ net-tools iproute2 tini \ && rm -rf /var/lib/apt/lists/* RUN useradd -m -d /home/steam -s /bin/bash steam \ && mkdir -p ${STEAMCMDDIR} ${SERVERDIR} /opt/scripts \ && chown -R steam:steam /home/steam ${STEAMCMDDIR} ${SERVERDIR} /opt/scripts USER steamWORKDIR ${STEAMCMDDIR}RUN set -eux; \ curl -L -o steamcmd_linux.tar.gz https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz; \ tar -xvzf steamcmd_linux.tar.gz; rm steamcmd_linux.tar.gz USER rootCOPY start.sh /opt/scripts/start.shRUN chmod +x /opt/scripts/start.sh && chown steam:steam /opt/scripts/start.sh USER steamWORKDIR /home/steamENTRYPOINT ["/usr/bin/tini","--"]CMD ["/opt/scripts/start.sh"]DOCKER
Then, create a start.sh file, which installs or updates the server and then launches it:
cat >/opt/game-servers/steam-generic/start.sh <<'BASH'set -euo pipefail : "${APP_ID:?APP_ID not set}": "${APP_DIR:=serverfiles}": "${VALIDATE:=false}": "${ADDITIONAL_STEAMCMD_ARGS:=}": "${START_CMD:?START_CMD not set}" STEAMCMDDIR="${STEAMCMDDIR:-/opt/steamcmd}"SERVERDIR="${SERVERDIR:-/home/steam/serverfiles}" echo "[INFO] Running SteamCMD update/install for app ${APP_ID} (validate=${VALIDATE})" VALIDATE_FLAG=""if [ "${VALIDATE}" = "true" ]; then VALIDATE_FLAG="validate"fi "${STEAMCMDDIR}/steamcmd.sh" +@sSteamCmdForcePlatformType linux \ +login anonymous \ +force_install_dir "${SERVERDIR}" \ +app_update "${APP_ID}" ${VALIDATE_FLAG} ${ADDITIONAL_STEAMCMD_ARGS} \ +quit echo "[INFO] Launching: ${START_CMD}"cd "${SERVERDIR}"exec bash -lc "${START_CMD}"BASH
Now set up a docker-compose.yml with correct APP_ID, START_CMD, and ports with the command below:
1cat >/opt/game-servers/steam-generic/docker-compose.yml <<'YAML'2services:3 steam-server:4 build: .5 container_name: steam-server6 restart: unless-stopped7 networks:8 - games-net9 environment:10 TZ: ${TZ:-UTC}11 12 APP_ID: "730" 13 APP_DIR: "serverfiles"14 VALIDATE: "false" 15 ADDITIONAL_STEAMCMD_ARGS: "" 16 17 18 START_CMD: "./game/bin/server_binary -dedicated +map de_inferno -tickrate 128 -gameport 27015 -ip 0.0.0.0"19 volumes:20 - ./data:/home/steam/serverfiles21 ports:22 - "27015:27015/udp"23 - "27015:27015/tcp"24 - "27020:27020/udp"25 26networks:27 games-net:28 external: true29YAML
Adjust the firewall rules with the following commands:
sudo ufw allow 27015/tcpsudo ufw allow 27015/udpsudo ufw allow 27020/udp
Start the server with the commands below:
cd /opt/game-servers/steam-genericset -a; . ../.env 2>/dev/null || true; set +adocker compose up -d --builddocker compose logs -f
Note: For host networking for lower latency and fewer NAT quirks, you can change the compose service:
1network_mode: "host"2ports: []
Then, remove the external network under that service. With host mode, open the exact ports on the VPS firewall because there’s no Docker NAT. To do this, you can run:
sudo ufw allow 27015/tcpsudo ufw allow 27015/udpsudo ufw allow 27020/udp
Deploy Palworld or Valheim Variant
You can use the same Steam template for different games by changing ports, START_CMD, and APP_ID.
Open required firewall ports and start the server:
sudo ufw allow 8211/udpsudo ufw allow 27015/udpdocker compose up -d --build
For Valheim, typical ports are 2456–2458/udp:
sudo ufw allow 2456:2458/udpdocker compose up -d --build
Backup Game Servers in VPS
You must consider protecting game data and keeping servers updated. To create backups, you can use the simple tar command.
For Minecraft:
cd /opt/game-servers/minecraftsudo tar czf "/opt/game-servers/backups/mc-$(date +%F-%H%M).tar.gz" data
For the Steam generic server:
cd /opt/game-servers/steam-genericsudo tar czf "/opt/game-servers/backups/steam-$(date +%F-%H%M).tar.gz" data
You can automate the backups with a cron job, for example, take backups daily at 4:
crontab -e0 4 * * * tar czf /opt/game-servers/backups/mc-$(date +\%F-\%H\%M).tar.gz -C /opt/game-servers/minecraft data15 4 * * * tar czf /opt/game-servers/backups/steam-$(date +\%F-\%H\%M).tar.gz -C /opt/game-servers/steam-generic data
To keep your Minecraft game server updated, you can pull and recreate the image with the following commands:
cd /opt/game-servers/minecraftdocker compose pull && docker compose up -d
For the Steam server, the start.sh runs app_update every start. If the Dockerfile has changed, rebuild and recreate it:
cd /opt/game-servers/steam-genericdocker compose build --no-cache && docker compose up -d
Docker keeps container logs at /var/lib/docker/containers/.../json.log. You can avoid huge log files with the following config under each service:
1logging:2 driver: json-file3 options:4 max-size: "50m"5 max-file: "3"
Resource Limits and Stability in Game Servers
You can prevent a single game server from using too much CPU or RAM by adding the following config:
1deploy:2 resources:3 limits:4 cpus: '2.0'5 memory: 6g
Note: deploy is used by Swarm. For Compose alone, some runtimes still apply cgroup limits. Alternatively, you can use:
Compose v2 still accepts these service-level keys for cgroup constraints.
Conclusion
By following this guide, you have successfully transformed a basic Ubuntu VPS into a powerful, organized, and secure platform for hosting multiple game servers using Docker.
We hope you enjoy this Hosting Game Servers Using Docker on a VPS guide. Subscribe to our X and Facebook channels to get the latest articles.
For further reading:
Low Latency Game Servers: Guide to Faster Performance
Discover High-Performance Servers for Global Clients