The Ultimate Guide to Hosting Docker Game Servers on a VPS

Hosting Game Servers Using Docker on a VPS

The Ultimate Guide to Hosting Docker Game Servers on a VPS

Hosting game servers using Docker on a VPS makes the process 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/Dubai
sudo timedatectl status

To configure UFW firewall rules, we only allow SSH now and configure game ports later:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH

Enable UFW firewall and check its status with the commands below:

sudo ufw enable
sudo 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 update
sudo 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 version
docker 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 the default, which is optional, with the command below:

cat >/opt/game-servers/.env <<'EOF'
TZ=Asia/Dubai
PUID=1000
PGID=1000
EOF

The .env simplifies environment variables for all servers.

Note: To get your real user’s 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 itzg/minecraft-server image for easy setup.

Create a docker-compose.yml in /opt/game-servers/minecraft/ with the following command:

cat >/opt/game-servers/minecraft/docker-compose.yml <<'YAML'
services:
  mc:
    image: itzg/minecraft-server:latest
    container_name: mc
    restart: unless-stopped
    networks:
      - games-net
    ports:
      - "25565:25565/tcp"
    environment:
      EULA: "TRUE"
      TZ: ${TZ:-UTC}
      # Choose one of these types; PAPER is a great default:
      TYPE: PAPER
      # Version (optional; empty = latest compatible):
      VERSION: "1.21.1"
      # Memory limits inside JVM:
      MEMORY: "4G"
      # Optional: enable online mode (true = Mojang auth). For testing on LAN, set to FALSE.
      ONLINE_MODE: "TRUE"
      # Optional: initial server properties:
      MOTD: "Dockerized Minecraft Server"
      ENABLE_COMMAND_BLOCK: "true"
    volumes:
      - ./data:/data
      - /etc/localtime:/etc/localtime:ro
    # Healthcheck for restart logic (optional)
    healthcheck:
      test: ["CMD", "mc-monitor", "status", "--host", "localhost"]
      interval: 30s
      timeout: 10s
      retries: 6

networks:
  games-net:
    external: true
YAML

Open firewall port for it with the command below:

sudo ufw allow 25565/tcp

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 +a
docker compose up -d

You can check for logs using this command:

docker compose logs -f

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, etc. by setting env 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

# Base deps
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/*

# Create steam user and dirs
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

# Install SteamCMD
USER steam
WORKDIR ${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

# Copy start script
USER root
COPY start.sh /opt/scripts/start.sh
RUN chmod +x /opt/scripts/start.sh && chown steam:steam /opt/scripts/start.sh

USER steam
WORKDIR /home/steam
ENTRYPOINT ["/usr/bin/tini","--"]
CMD ["/opt/scripts/start.sh"]
DOCKER

Then, create a start.sh file, which installs and updates the server and then launches it:

cat >/opt/game-servers/steam-generic/start.sh <<'BASH'
#!/usr/bin/env 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:

cat >/opt/game-servers/steam-generic/docker-compose.yml <<'YAML'
services:
  steam-server:
    build: .
    container_name: steam-server
    restart: unless-stopped
    networks:
      - games-net
    environment:
      TZ: ${TZ:-UTC}
      # --- COMMON ENV ---
      APP_ID: "730"        # EXAMPLE: replace with your game's dedicated server AppID
      APP_DIR: "serverfiles"
      VALIDATE: "false"    # set "true" occasionally
      ADDITIONAL_STEAMCMD_ARGS: ""  # e.g. "-beta public -betapassword yourpass"
      # START_CMD MUST be adapted to your title:
      # Example (Source-style — replace map, gamemode, etc.):
      START_CMD: "./game/bin/server_binary -dedicated +map de_inferno -tickrate 128 -gameport 27015 -ip 0.0.0.0"
    volumes:
      - ./data:/home/steam/serverfiles
    # Replace these with the actual required ports for your game:
    ports:
      - "27015:27015/udp"
      - "27015:27015/tcp"
      - "27020:27020/udp"

networks:
  games-net:
    external: true
YAML

Adjust the firewall rules with the following commands:

sudo ufw allow 27015/tcp
sudo ufw allow 27015/udp
sudo ufw allow 27020/udp

Start the server with the commands below:

cd /opt/game-servers/steam-generic
set -a; . ../.env 2>/dev/null || true; set +a
docker compose up -d --build
docker compose logs -f

Note: For host networking for lower latency and fewer NAT quirks, you can change the compose service:

network_mode: "host"
ports: [] # remove ports section when using host mode

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/tcp
sudo ufw allow 27015/udp
sudo 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.

# Edit docker-compose.yml START_CMD and ports for Palworld:
# START_CMD example (adjust path/binary per server files layout):
# START_CMD: "./Pal/Binaries/Linux/PalServer-Linux-Shipping PalWorldSettings.ini -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS -publicip=0.0.0.0 -port=8211 -QUERYPORT=27015"
# Ports:
#   - "8211:8211/udp"
#   - "27015:27015/udp"

Open required firewall ports and start the server:

sudo ufw allow 8211/udp
sudo ufw allow 27015/udp
docker compose up -d --build

For Valheim, typical ports 2456–2458/udp:

# START_CMD: "./valheim_server.x86_64 -name \"MyDockerValheim\" -port 2456 -world \"DockerWorld\" -password \"S3cret\" -public 1"
# Ports:
#   - "2456:2456/udp"
#   - "2457:2457/udp"
#   - "2458:2458/udp"

sudo ufw allow 2456:2458/udp
docker 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/minecraft
sudo tar czf "/opt/game-servers/backups/mc-$(date +%F-%H%M).tar.gz" data

For Steam generic:

cd /opt/game-servers/steam-generic
sudo 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 -e
# Add lines (adjust paths):
0 4 * * * tar czf /opt/game-servers/backups/mc-$(date +\%F-\%H\%M).tar.gz -C /opt/game-servers/minecraft data
15 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/minecraft
docker 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-generic
docker 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:

logging:
  driver: json-file
  options:
    max-size: "50m"
    max-file: "3"

Resource Limits and Stability in Game Servers

You can prevent a single game server from CPU or RAM high usage by adding the following config:

    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 6g

Note: deploy is used by Swarm; for Compose alone, some runtimes still apply cgroup limits. Alternatively, you can use:

mem_limit: 6g
cpus: 2.0

Compose v2 still accepts these service-level keys for cgroup constraints.

FAQs

Why should I use Docker for game servers instead of installing them directly on the VPS?

Docker provides isolation, ensuring that each game server runs in its own contained environment. This prevents conflicts between dependencies, makes updates and rollbacks easier, and simplifies management.

What’s the difference between Bridge and Host Docker networking mode?

Bridge Mode (Default): Docker creates a private internal network for containers, and you must manually map ports. This is more secure and ideal for running multiple game servers on one VPS.
Host Mode: The container shares the VPS’s network stack directly, eliminating the NAT layer. This can reduce latency and solve connectivity issues for some Steam/Source games, but it is less isolated.
It is recommended to start with Bridge mode and switch to Host only if you experience problems.

How do I find the correct APP_ID for a Steam game server?

The best resource is the official SteamDB site. Search for your game and look for the “App ID” of the dedicated server tool, not the client game.

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 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

Post Your Comment

PerLod delivers high-performance hosting with real-time support and unmatched reliability.

Contact us

Payment methods

payment gateway
Perlod Logo
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.