/docker-sourceservers

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

Primary LanguagePowerShellApache License 2.0Apache-2.0

docker-sourceservers

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

master
pipeline-github-master-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 2 / Source Engine (SRCDS)

Game Image Tag v<tag> Size Status
Counter-Strike 2 sourceservers/cs2 srcds-cs2-version-badge srcds-cs2-size-badge pipeline-gitlab-srcds-cs2-badge
Counter-Strike: Global Offensive sourceservers/csgo srcds-csgo-version-badge srcds-csgo-size-badge pipeline-gitlab-srcds-csgo-badge
Counter-Strike: Source sourceservers/cstrike srcds-cstrike-version-badge srcds-cstrike-size-badge pipeline-gitlab-srcds-cstrike-badge
Day of Defeat: Source sourceservers/dod srcds-dod-version-badge srcds-dod-size-badge pipeline-gitlab-srcds-dod-badge
Half-Life 2: Deathmatch sourceservers/hl2mp srcds-hl2mp-version-badge srcds-hl2mp-size-badge pipeline-gitlab-srcds-hl2mp-badge
Left 4 Dead sourceservers/left4dead srcds-left4dead-version-badge srcds-left4dead-size-badge pipeline-gitlab-srcds-left4dead-badge
Left 4 Dead 2 sourceservers/left4dead2 srcds-left4dead2-version-badge srcds-left4dead2-size-badge pipeline-gitlab-srcds-left4dead2-badge
Team Fortress 2 sourceservers/tf srcds-tf-version-badge srcds-tf-size-badge pipeline-gitlab-srcds-tf-badge

Goldsource Engine (HLDS)

Game Image Tag v<tag> Size Status
Counter-Strike 1.6 goldsourceservers/cstrike hlds-cstrike-version-badge hlds-cstrike-size-badge pipeline-gitlab-hlds-cstrike-badge
Counter-Strike: Condition Zero goldsourceservers/czero hlds-czero-version-badge hlds-czero-size-badge pipeline-gitlab-hlds-czero-badge
Deathmatch Classic goldsourceservers/dmc hlds-dmc-version-badge hlds-dmc-size-badge pipeline-gitlab-hlds-dmc-badge
Day of Defeat goldsourceservers/dod hlds-dod-version-badge hlds-dod-size-badge pipeline-gitlab-hlds-dod-badge
Opposing Force goldsourceservers/gearbox hlds-gearbox-version-badge hlds-gearbox-size-badge pipeline-gitlab-hlds-gearbox-badge
Ricochet goldsourceservers/ricochet hlds-ricochet-version-badge hlds-ricochet-size-badge pipeline-gitlab-hlds-ricochet-badge
Team Fortress Classic goldsourceservers/tfc hlds-tfc-version-badge hlds-tfc-size-badge pipeline-gitlab-hlds-tfc-badge
Half-Life goldsourceservers/valve hlds-valve-version-badge hlds-valve-size-badge pipeline-gitlab-hlds-valve-badge

Image Info

Game versions & tags

A layered image of a game is but a clean image compounded with game update layers. Clean images are tagged by <version>, while layered images are tagged by <version>-layered and <version>-layered-<date>.

The latest tag of each game points to the game's newest layered image unless a newer clean image has been built with the latest tag. Thus, by using the latest tag, layered images are almost always used, circumventing the need to pull entire clean images for obtaining game updates.

Image size

Image sizes shown above or on Docker Hub correspond to their compressed size. Actual sizes after pulling are approximately 2x the compressed size.

Update duration

From the moment a game update is released, the time taken before the game's images are built and available for pulling largely depends on the size of the game. For instance, layered and clean images typically take under 15 and over 40 minutes respectively for Counter-Strike 2, but under 5 minutes each for Counter-Strike 1.6.

Build cache is used where possible to minimize update durations.

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.

Usage

Disclaimer: The project assumes knowledge concerning use of the docker runtime. Also, instructions on customization and orchestration of containerized game instances are beyond the scope the project.

Docker

The following are some guidelines on usage of the provided images with docker. The same guidelines should also apply to container orchestration tools such as Kubernetes, Docker Swarm Mode, and the standalone tool, Docker Compose.

ENTRYPOINT and CMD

The default ENTRYPOINT for all game images is "bash", "-c", and the CMD is "". These values allow a string of initialization commands to be executed before invocation of the game binary, similar to what is commonly achieved with docker-entrypoint.sh, or through multi-line commands in container manifests.

While the default values may not always be intuitive, they can be overridden with the docker run --entrypoint parameter, or through their respective configuration options in container manifests. Alternatively, they can be modified with custom built images.

WORKDIR

The default work directory for all the images is /server within which all of a game's files reside.

Starting

Via command-line
# Counter-Strike 2
## Via default entrypoint (/bin/bash -c)
docker run -it --rm -p 27015:27015/tcp -p 27015:27015/udp sourceservers/cs2:latest 'game/bin/linuxsteamrt64/cs2 -dedicated -port 27015 +game_type 0 +game_mode 1 +mapgroup mg_active +map de_dust2'
docker run -it --rm -p 27015:27015/tcp -p 27015:27015/udp sourceservers/cs2:latest 'printenv && ls -al && exec game/bin/linuxsteamrt64/cs2 -dedicated -port 27015 +game_type 0 +game_mode 1 +mapgroup mg_active +map de_dust2'
## Via custom entrypoint (game binary)
docker run -it --rm -p 27015:27015/tcp -p 27015:27015/udp --entrypoint game/bin/linuxsteamrt64/cs2 sourceservers/cs2:latest -dedicated -port 27015 +game_type 0 +game_mode 1 +mapgroup mg_active +map de_dust2
## Via custom entrypoint (/bin/bash)
docker run -it --rm -p 27015:27015/tcp -p 27015:27015/udp --entrypoint /bin/bash sourceservers/cs2:latest -c 'printenv && ls -al && exec game/bin/linuxsteamrt64/cs2 -dedicated -port 27015 +game_type 0 +game_mode 1 +mapgroup mg_active +map de_dust2'

# Counter-Strike: Global Offensive
## Via default entrypoint (/bin/bash -c)
docker run -it --rm -p 27016:27016/tcp -p 27016:27016/udp sourceservers/csgo:latest 'srcds_linux -game csgo -port 27016 +game_type 0 +game_mode 0 +mapgroup mg_active +map de_dust2'
docker run -it --rm -p 27016:27016/tcp -p 27016:27016/udp sourceservers/csgo:latest 'printenv && ls -al && exec srcds_linux -game csgo -port 27016 +game_type 0 +game_mode 0 +mapgroup mg_active +map de_dust2'
## Via custom entrypoint (game binary)
docker run -it --rm -p 27016:27016/tcp -p 27016:27016/udp --entrypoint srcds_linux sourceservers/csgo:latest -game csgo -port 27016 +game_type 0 +game_mode 0 +mapgroup mg_active +map de_dust2
## Via custom entrypoint (/bin/bash)
docker run -it --rm -p 27016:27016/tcp -p 27016:27016/udp --entrypoint /bin/bash sourceservers/csgo:latest -c 'printenv && ls -al && exec srcds_linux -game csgo -port 27016 +game_type 0 +game_mode 0 +mapgroup mg_active +map de_dust2'

# Counter-Strike 1.6
## Via default entrypoint (/bin/bash -c)
docker run -it --rm -p 28015:28015/udp goldsourceservers/cstrike:latest 'hlds_linux -game cstrike +port 28015 +maxplayers 10 +map de_dust2'
docker run -it --rm -p 28015:28015/udp goldsourceservers/cstrike:latest 'printenv && ls -al && exec hlds_linux -game cstrike +port 28015 +maxplayers 10 +map de_dust2'
## Via custom entrypoint (game binary)
docker run -it --rm -p 28015:28015/udp --entrypoint hlds_linux goldsourceservers/cstrike:latest -game cstrike +port 28015 +maxplayers 10 +map de_dust2
## Via custom entrypoint (/bin/bash)
docker run -it --rm -p 28015:28015/udp --entrypoint /bin/bash goldsourceservers/cstrike:latest -c 'printenv && ls -al && exec hlds_linux -game cstrike +port 28015 +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
Via manifests

For a declarative approach, game server environments can be defined within container manifests such as docker-compose.yml which can then be used for managing instances:

# Via docker-compose
docker-compose up

Attaching

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

Debugging

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

Updating

To update a game server, simply initiate a pull for the game image by the latest tag and recreate the container.

There are many ways to detect when a game server needs an update but which are beyond the scope of the project. Here is an example for utilizing a cronjob for updating a container.

Important Considerations

Due to the variety of SRCDS and HLDS games and the various ways each of them can or have to be hosted, the images built with this project are kept to be as generic as possible. The following are some important considerations concerning the built images.

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.

Including a generic, conventional docker-entrypoint.sh script would unlikely adequately serve operators given the various possible setups that could differ widely across games, game modes, mods, and plugins. Operators would be better off implementing their own custom entrypoint scripts without having to accommodate pre-included ones in the design of their setups.

This leads us to the next and 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 twelve-factor methodology - 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 wherein it is common for their docker images to include an entrypoint script which maps environment variables onto command line arguments for invocation.

Source 2, Source, and Goldsource games belong to the group of applications that do not read from environment variables but that are instead configured via parameters (i.e. flags beginning with -, e.g. -usercon, see Source 2 parameters / 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).

Because of the many parameters and Cvars that exist for each game and mod/plugin setup, it does not make sense to map them directly to environment variables for several reasons: First, doing so introduces an unnecessary layer of abstraction which operators would have to learn on top of the numerous available parameters and Cvars. 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 a given 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.

Building a non-root user into the images 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, and rendering the 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.

Invocation via binary vs wrapper script

Source 2

Newly launched games such as Counter-Strike 2 include only the game binary for invocation.

Source / Goldsource

The official games from Valve include a binary and a wrapper script as part of the game files, both of which reside in the game's root directory.

The game binary:

  • srcds_linux (Source)
  • hlds_linux (Goldsource)

The wrapper script, commonly used in non-containerized setups:

  • srcds_run (Source)
  • hlds_run (Goldsource)

Invoking the game binary directly is the recommended choice especially when hosting the game server within containers. Doing so ensures the game process is run as PID 1 which in turn ensures the game process functions optimally within a container.

Some operators may choose to invoke the wrapper script instead as it provides features such as auto-restart and auto-update. Doing so presents several problems related to container infrastucture: First, invoking the wrapper script prevents the game process from being run as PID 1 which introduces unpredictable behavior pertaining to the container. Second, using the wrapper script auto-restart feature overlaps with restart functionalities already provided by container orchestration tools, introducing the issue of unpredictable restarts to the container. Third, using the wrapper script 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 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 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.