Skip to content
Source / Goldsource dedicated server images built through use of steamcmd. 🐳
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.


Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci Fix CircleCI notification job sleep duration to use variable value (#11) Mar 4, 2020


Source / Goldsource dedicated server images built through use of steamcmd.

Build Update
pipeline-travis-build-badge pipeline-azurepipelines-build-badge pipeline-circleci-build-badge pipeline-travis-update-badge pipeline-azurepipelines-update-badge pipeline-circleci-update-badge

Supported Tags

Game Images

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.

srcds-dockerhub-badge hlds-dockerhub-badge

Source Engine (SRCDS)

Game Image Tag Size / Layers
Counter-Strike: Global Offensive sourceservers/csgo srcds-csgo-version-badge srcds-csgo-layers-badge
Counter-Strike: Source sourceservers/cstrike srcds-cstrike-version-badge srcds-cstrike-layers-badge
Day of Defeat: Source sourceservers/dod srcds-dod-version-badge srcds-dod-layers-badge
Half-Life 2: Deathmatch sourceservers/hl2mp srcds-hl2mp-version-badge srcds-hl2mp-layers-badge
Left 4 Dead sourceservers/left4dead srcds-left4dead-version-badge srcds-left4dead-layers-badge
Left 4 Dead 2 sourceservers/left4dead2 srcds-left4dead2-version-badge srcds-left4dead2-layers-badge
Team Fortress 2 sourceservers/tf srcds-tf-version-badge srcds-tf-layers-badge

Goldsource Engine (HLDS)

Game Image Tag Size / Layers
Counter-Strike 1.6 goldsourceservers/cstrike hlds-cstrike-version-badge hlds-cstrike-layers-badge
Counter-Strike: Condition Zero goldsourceservers/czero hlds-czero-version-badge hlds-czero-layers-badge
Deathmatch Classic goldsourceservers/dmc hlds-dmc-version-badge hlds-dmc-layers-badge
Day of Defeat goldsourceservers/dod hlds-dod-version-badge hlds-dod-layers-badge
Opposing Force goldsourceservers/gearbox hlds-gearbox-version-badge hlds-gearbox-layers-badge
Ricochet goldsourceservers/ricochet hlds-ricochet-version-badge hlds-ricochet-layers-badge
Team Fortress Classic goldsourceservers/tfc hlds-tfc-version-badge hlds-tfc-layers-badge
Half-Life goldsourceservers/valve hlds-valve-version-badge hlds-valve-layers-badge

Image Info

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.

The 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 <version>-layered.

Image size

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.

Update duration

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 Counter-Strike 1.6.

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.

Build history

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.


Currently, the default ENTRYPOINT for all game images is "bash", "-c", and the CMD is "". 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'
  • -t for a pseudo-TTY is mandatory; servers may not run correctly without it
  • -i for STDIN for interactive use of the game console
  • -d for 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.

Important Considerations

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.

Base images

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.

Entrypoint script

The game images do not include an entrypoint script.

While the conventional 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.

Environment variables

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 -, e.g. -usercon, see SRCDS parameters and HLDS parameters), as well as Cvars (i.e. flags beginning with +, e.g. +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 left4dead and left4dead2), as well as mod/plugin-specific (e.g. sourcemod, amxmodx, garrysmod).

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 script, introducing a lot of unnecessary builds; Third, the very 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 server.cfg.

Non-root user

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:

  • srcds_run (Source)
  • hlds_run (Goldsource)

The game binary:

  • srcds_linux (Source)
  • hlds_linux (Goldsource)

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.

You can’t perform that action at this time.