Source / Goldsource dedicated server images built through use of steamcmd
.
master |
---|
latest
(/build/Dockerfile), (/update/Dockerfile)<version>
(/build/Dockerfile)<version>-layered
(/update/Dockerfile)<version>-layered-<date>
(/update/Dockerfile)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.
Game | Image | Tag v<tag> |
Size | Status |
---|---|---|---|---|
Counter-Strike 2 | sourceservers/cs2 |
|||
Counter-Strike: Global Offensive | sourceservers/csgo |
|||
Counter-Strike: Source | sourceservers/cstrike |
|||
Day of Defeat: Source | sourceservers/dod |
|||
Half-Life 2: Deathmatch | sourceservers/hl2mp |
|||
Left 4 Dead | sourceservers/left4dead |
|||
Left 4 Dead 2 | sourceservers/left4dead2 |
|||
Team Fortress 2 | sourceservers/tf |
Game | Image | Tag v<tag> |
Size | Status |
---|---|---|---|---|
Counter-Strike 1.6 | goldsourceservers/cstrike |
|||
Counter-Strike: Condition Zero | goldsourceservers/czero |
|||
Deathmatch Classic | goldsourceservers/dmc |
|||
Day of Defeat | goldsourceservers/dod |
|||
Opposing Force | goldsourceservers/gearbox |
|||
Ricochet | goldsourceservers/ricochet |
|||
Team Fortress Classic | goldsourceservers/tfc |
|||
Half-Life | goldsourceservers/valve |
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 sizes shown above or on Docker Hub correspond to their compressed size. Actual sizes after pulling are approximately 2x the compressed size.
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.
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 use of the docker
runtime. Also, instructions on customization and orchestration of containerized game instances are beyond the scope the project.
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.
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.
The default work directory for all the images is /server
within which all of a game's files reside.
# 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 consoleFor 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
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 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.
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.
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.
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.
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
.
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.
Newly launched games such as Counter-Strike 2
include only the game binary for invocation.
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.