Source / Goldsource dedicated server images built through use of
Dedicated servers hosted on Steam are usually required to be running the latest version of the game in order for clients to connect to them. Simply use the
latest tag for the latest version of a game.
Source Engine (SRCDS)
|Game||Image||Tag||Size / Layers|
|Counter-Strike: Global Offensive||
|Day of Defeat: Source||
|Half-Life 2: Deathmatch||
|Left 4 Dead||
|Left 4 Dead 2||
|Team Fortress 2||
Goldsource Engine (HLDS)
|Game||Image||Tag||Size / Layers|
|Counter-Strike: Condition Zero||
|Day of Defeat||
|Team Fortress Classic||
Game versions & tags
Both a new clean and layered image of a game are built on an available game update. Due to the nature of Docker images, an image cannot exactly be updated; any modifications to it adds to its existing layers.
latest tag follows a layered approach to updating. Using it prevents the need to pull the newest clean image of a game on each available update. However, layered images gradually grow in size with increasing update layers. To solve this, the
latest tag is made to automatically reference the clean image of a game on the next update upon reaching 1.75x its initial size.
Clean images are tagged by
<version>. Layered images are tagged by
Image sizes shown above or on Docker Hub correspond to an image's compressed size. Actual sizes vary, but are approximately 2x larger after pulling an image.
From the moment Valve issues an update, the time taken before a game's images are built and available for pulling largely depends on the size of the game. For instance, layered and clean images take over 15 and 40 minutes respectively for
Counter-Strike: Global Offensive, but under 5 minutes each for
While the use of build cache can help drastically reduce update durations, it cannot be utilized as the game images are built for public use, purposefully done so using public machines.
The project uses multiple CI services for its build jobs. You can find the history of past build jobs by clicking on their corresponding build status badges.
Disclaimer: The project assumes knowledge concerning the
docker runtime. Instructions on customization and orchestration of containerized game instances are beyond the scope the project.
The following are some guidelines on how to use the provided images with
docker that should also apply to container orchestration tools should operators wish to use them for hosting container workloads.
ENTRYPOINT and CMD
Currently, the default
ENTRYPOINT for all game images is
"bash", "-c", and the
"". These values make it convenient especially in development environments where the game's command line can simply be appended as the final argument to the
docker run command for starting a server.
Each of the default values can also be overridden at runtime, a feature well supported by container orchestration tools such as Kubernetes and Docker Swarm Mode, and the standalone tool, Docker Compose. Alternatively, they can be modified as part of the build steps in custom images.
The default working directory for all the images is
/server within which all of a game's files reside.
The following are some examples of how the game servers can be started:
# Counter-Strike: Global Offensive docker run -it -p 27015:27015/udp sourceservers/csgo:latest 'srcds_linux -game csgo -port 27015 +game_type 0 +game_mode 1 +mapgroup mg_active +map de_dust2' # Counter-Strike 1.6 docker run -it -p 27016:27016/udp goldsourceservers/cstrike:latest 'hlds_linux -game cstrike +port 27016 +maxplayers 10 +map de_dust2'
-tfor a pseudo-TTY is mandatory; servers may not run correctly without it
-ifor STDIN for interactive use of the game console
-dfor running the container in detached mode
If the game process is running as
PID 1 and STDIN is enabled for the container, the game's console can be accessed via:
docker attach containername
To debug a container or its files:
# To enter into a container docker exec -it containername bash # To issue detached command(s) docker exec containername ps aux # Single, simple command docker exec containername bash -c 'printenv && ls -al && ps aux' # Multiple or advanced commands
To update a gameserver, simply initiate a pull for the game image by the
latest tag and restart the server.
docker pull sourceservers/csgo:latest docker rm -f csgo-server docker run --name csgo-server -it -p 27015:27015/udp sourceservers/csgo:latest 'srcds_linux -game csgo -port 27015 +game_type 0 +game_mode 1 +mapgroup mg_active +map de_dust2'
There are many ways to detect when a gameserver needs an update but that is out of the scope of the project. Here is a simple example for utilizing a
cronjob for updating a container.
Due to the variety of SRCDS and HLDS games that can be hosted and the various ways each of the games can and/or have to be hosted, the images built using this project are kept to be as generic as possible. The following are some important considerations concerning the images provided by the project.
The game images are based on the images built via the project
startersclan/docker-steamcmd. Issues or pull requests that are potentially applicable across the game images such as those pertaining to the OS, administrative tools, or game dependencies are to be directed to that project instead.
The game images do not include an entrypoint script.
While the conventional
docker-entrypoint.sh could have been included in the game images, having so also takes away flexibility in how the images can be used. Operators wishing to utilize their own entrypoint scripts would have to include removal of pre-existing ones as part of their build or initialization processes which increases administrative overhead. Moreover, a generic entrypoint script is unlikely to adequately serve operators given the various possible setups that could differ widely across games, game modes, mods, and plugins.
This brings us to the next but a much related consideration.
The game images do not include support for configuring game instances via environment variables.
Docker images are often packaged with applications designed to comply with the twelve-factor methodology - Environment as config where environment variables are read directly as configuration by the application, a case in point being the docker registry. Some applications however do not read environment variables as configuration but instead accept command line arguments or read from config files, where it is then common for their docker images to include an entrypoint script which maps environment variables onto command line arguments for invocation.
Source and Goldsource games belong to the group of applications that do not read from environment variables and that are instead configured via parameters (i.e. flags beginning with
-usercon, see SRCDS parameters and HLDS parameters), as well as Cvars (i.e. flags beginning with
+sv_lan 0, see SRCDS console variables and HLDS console variables). Although there are many Cvars shared across SRCDS and HLDS games, there are also Cvars that are game-specific (e.g. the many hundreds for
left4dead2), as well as mod/plugin-specific (e.g.
Because of the malleable nature of how Cvars are used, it does not make sense to map them directly to environment variables for several reasons: First, it introduces an unnecessary layer of abstraction which operators would have to learn on top of the numerous available parameters and Cvars for each game; Second, a single change to any envvar-cvar mapping will require a rebuild of the docker image to contain the new
docker-entrypoint.sh script, introducing a lot of unnecessary builds; Third, the very
docker-entrypoint.sh script providing the envvar-cvar mapping would also require versioning, introducing yet another burden on top of just keeping the images updated.
As such, the provided images do not support configuration via environment variables. The recommended approach would be to specify all necessary launch parameters and Cvars for the game server within the container's command, and all other Cvars including those containing secret values within mounted or init-time provisioned configuration file(s), such as
The game images do not include a non-root user.
The images as aforementioned are meant to be generic. Having a non-root user poses a problem especially when volumes are going to be used by operators. A common
UID built into the images would unlikely fulfill the requirements of operators whose hosts would then require a matching
UID in cases where bind mounts are used. A mismatch or missing
UID within the container or the host would prevent the container user from accessing the data on the volumes, leading to issues pertaining to the game server, rendering the game images useless unless customized.
Operators who wish to run the game servers under a non-root user can customize the provided images with a non-root user with a
UID of their choice.
Note: A non-root user could be added to the images in the future if the addition is sufficiently requested with good reasons for its implementation, or could continue to be left out from the images. Best practices can change depending on features provided by newer versions of container runtimes and/or orchestrators.
Invocation via wrapper script vs binary
The official games from Valve come with a wrapper script and a binary as part of the game files, both of which reside in the game's root directory.
The wrapper script, commonly used in non-containerized setups:
The game binary:
Invoking the game binary directly is the recommended choice especially when hosting the game server within containers. Doing so allows the game process to run as
PID 1, which ensures the game's console output are correctly propagated as container logs, and makes attaching of the terminal to the game's console possible for interactive administration.
Some operators may choose to invoke the wrapper script instead as it provides features such as auto-restart and auto-updates. Note that doing so presents several problems related to container infrastucture. First, invoking the wrapper script alone prevents the game process from being run as
PID 1 and in so introduces unpredictable behavior to the container. Second, using the auto-restart feature adds overlapping restart functionalities already provided by container orchestration tools that could potentially introduce conflicting container restart behaviors. Third, using the auto-update feature introduces mutation to the container's supposed game version on available updates, wherein changes would not only be lost upon container deletion, but that would have to be performed for every new container started from outdated game images, contradicting the principle of immutability in container design.
As such, invocation via the wrapper script is strongly discouraged, and support for doing so will not be a priority in this project. The provided game images being generic however should not prevent operators from adopting such approaches should they wish to.