In this tutorial you will learn how to run game servers in a Docker environment, specifically a Counter Strike: Global Offensive server that can automatically update itself.
Connect to your server via SSH. If you have not set up SSH access yet, follow the instructions in the corresponding tutorial before continuing with this tutorial.
After connecting to your server, verify that your Docker installation is working correctly by running
docker run hello-world
This should show you some text:
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
If you don't see this specific message or an error, Docker is not running correctly. In this case, please follow the instructions in the Docker installation tutorial again (see the official documentation).
Also make sure that your user is in the Docker group.
Otherwise continue with Step 2, everything is set up.
First create a folder that will hold all files needed for the container.
mkdir ~/csgo-container
cd ~/csgo-container
In order to connect to your server over the internet, you need to acquire a game server login token, short GSLT, here. Technically this is optional, but since there are very few reasons for hosting a LAN-only game server on a server, this is strongly recommended.
If you want to play on workshop maps you also need to get the API key for your Steam account here.
Be careful not to share these with anyone since misuse can result in termination of your Steam account. It is often advisable to create a new empty Steam account just for your server. But that goes beyond the scope of this tutorial.
Since we want to create a CS:GO dedicated server, we first need to create some config files specifically for the source dedicated server - things like start options, game settings and passwords.
We do this by placing all required files in the folder we created and by copying them later to the container in Step 2.3.
First we need to create a runscript that allows the server to automatically update.
Simply create a file named runscript_csgo
that contains the following:
login anonymous
force_install_dir /data/csgo_ds
app_update 740
quit
Then we create a simple script that can start the server.
For this to work, create a file named start-csgo.sh
with the following content:
##!/usr/bin/bash
steamcmd/steamcmd.sh +login anonymous +force_install_dir /data/csgo_ds +app_update 740 +quit
sed -i -e 's#\./steam\.sh #\./steamcmd\.sh #g' csgo_ds/srcds_run # edit autoupdate for server version
csgo_ds/srcds_run -game csgo -console -usercon -port 27015 \
-net_port_try 1 -maxplayers_override 10 -tickrate 128 \
-nobreakpad -game csgo -console \
-usercon -secure -authkey "<API KEY HERE>" \
-autoupdate -steam_dir /data/steamcmd \
-steamcmd_script /data/steamcmd/runscript_csgo
As you can see, a lot of configuration happens when the server starts. Feel free to adapt these settings as you wish.
Also add your API key after the authkey
argument.
Next we create two files for the game settings.
First create autoexec.cfg
with the following content:
sv_setsteamaccount "<ENTER GSLT HERE>" // change this to your GSLT key
game_type 0
game_mode 1
sv_pure 1
// host_workshop_collection <WORKSHOP COL ID>
// workshop_start_map <WORKSHOP START MAP>
map de_dust2 // set default map here if not using a workshop map
log on
sv_allow_votes 0
sv_logbans 1
sv_logecho 1
sv_logfile 1
sv_log_onefile 1
sv_hibernate_when_empty 1
sv_hibernate_ms 5
sv_maxrate "0"
sv_minrate "128000"
sv_minupdaterate "128"
sv_mincmdrate "128"
sv_pure 1
sv_pure_kick_clients 1
hostname "My CSGO Server" // change this to your servers name (optional)
rcon_password "" // change this if you want to enter commands from the cs:go client (optional)
sv_password "" // change this to protect the server with a password (optional)
sv_cheats 0
sv_lan 0
exec banned_user.cfg
exec banned_ip.cfg
The autoexec.cfg
file is executed when the server first starts, here you can define your preferred password and hostname.
You can also set things like the workshop collection if you have some maps from the Steam workshop you want to play on.
But most importantly, you have to add your GSLT key that you generated for your server here. Insert the GSLT key after sv_setsteamaccount
in the first line. You can connect to your server over the internet if this key is present.
For all game modes and game types see this table from the Valve Developer Wiki - CS:GO Game Modes
game_type | game_mode | ||||
---|---|---|---|---|---|
0 | 1 | 2 | 3 | ||
Classic | 0 | Casual | Competitive | Wingman | Weapons Expert |
Gun Game | 1 | Arms Race | Demolition | Deathmatch | |
Training | 2 | Training | |||
Custom | 3 | Custom | |||
Cooperative | 4 | Guardian | Co-op Strike | ||
Skirmish | 5 | War Games |
|||
Free For All | 6 | Danger Zone |
Now create config.cfg
with the following content:
mp_autoteambalance 0
mp_limitteams 0
mp_autokick 0
writeid
writeip
These settings are executed every time a new map loads. As you can see, "Autokick" and "Team balancing" have been disabled. Feel free to change these to whatever you want to use.
A dockerfile describes all the inner workings of the container you want to create.
Starting with the FROM
instruction which tells Docker what our container will be based on.
In our case we will be using Ubuntu Focal.
Now create the dockerfile itself. Simply create a file named Dockerfile
in the folder we created and add the following:
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
ENV USER csgo
ENV HOME /data
ENV SERVER $HOME/csgo_ds
This sets some variables we will use later on.
Running a CS:GO dedicated server requires some packages. We will instruct the container to download the packages when it is first created. Just append the following instructions that install some required libraries and configure the English locales inside the container.
RUN apt update && \
apt install curl lib32gcc1 locales -y
RUN locale-gen en_US.UTF-8 \
&& update-locale LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 \
&& dpkg-reconfigure --frontend=noninteractive locales \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Running things as root is bad. But since we are running everything in a container this is actually not that big of a deal. The main reason for creating an unprivileged user is the fact that we don't want all files created in the container to be owned by root, which is annoying when extracting files from the container.
We can create a user and group for running the dedicated server by adding these lines to our dockerfile:
RUN addgroup --gid 1000 csgo \
&& adduser --system --shell /bin/false --uid 1000 --ingroup csgo --home /data csgo && \
chown csgo:csgo -R /data
WORKDIR /data
USER $USER
All config files for the CS:GO server now need to be copied into the container with the following instructions:
COPY --chown=1000:1000 runscript_csgo /data/steamcmd/runscript_csgo
COPY --chown=1000:1000 start-csgo.sh /data/start-csgo.sh
COPY --chown=1000:1000 *.cfg /data/csgo_ds/csgo/cfg/
RUN curl -sqL "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz" | tar -xzC /data/steamcmd
The last line simply downloads the steamcmd program from Valve, which allows us to download csgo and all other Steam games into our container.
Since all configuration is complete, we now can open ports and run our start-csgo.sh
that we created earlier.
Just add these lines, which expose the default ports for csgo and run the script:
EXPOSE 27015/tcp
EXPOSE 27015/udp
CMD [ "sh", "/data/start-csgo.sh" ]
At this point our dockerfile is done and ready to be deployed.
This should be the final dockerfile:
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
ENV USER csgo
ENV HOME /data
ENV SERVER $HOME/csgo_ds
RUN apt update && \
apt install curl lib32gcc1 locales -y
RUN locale-gen en_US.UTF-8 \
&& update-locale LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 \
&& dpkg-reconfigure --frontend=noninteractive locales \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN addgroup --gid 1000 csgo \
&& adduser --system --shell /bin/false --uid 1000 --ingroup csgo --home /data csgo && \
chown csgo:csgo -R /data
WORKDIR /data
USER $USER
COPY --chown=1000:1000 runscript_csgo /data/steamcmd/runscript_csgo
COPY --chown=1000:1000 start-csgo.sh /data/start-csgo.sh
COPY --chown=1000:1000 *.cfg /data/csgo_ds/csgo/cfg/
RUN curl -sqL "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz" | tar -xzC /data/steamcmd
EXPOSE 27015/tcp
EXPOSE 27015/udp
CMD [ "sh", "/data/start-csgo.sh" ]
Now all you need to do is
To build the container simply run:
docker build . -t csgo
and run:
docker run -d --restart=unless-stopped --name csgo \
-p 27015:27015/udp -p 27015:27015 -it -v csgo_data:/data csgo
to start the container with default ports. If you plan on running multiple containers or prefer different ports, you need to adjust the port mappings accordingly with the -p
argument.
Now the container should be started and will begin downloading the CS:GO dedicated server. This can take some minutes since there are about 20GB of server files to be downloaded.
You can see the logs by running:
docker logs -f csgo
You can also easily start, restart and stop the container by running:
docker start csgo
docker restart csgo
docker stop csgo
If you want to get shell access inside the container to edit config files, you can do that by running:
docker exec -it csgo bash
Alternatively you sometimes might need access to the command line interface for the CS:GO server if you don't want to use RCON. This can be done by attaching to the container by running:
## this is essentially the same as opening your console in the CS:GO client
docker attach csgo
To leave this console, press CTRL-P + Q.
Another useful thing are backups, which are not really needed in this example, since most of the server state is configured in the config files, but still can be useful. To copy files from the container to your computer, you can run:
docker cp csgo:/data/csgo_ds/csgo/cfg/<files to copy> .
You should have a running CS:GO dedicated server that keeps itself up to date.
All data is stored in the csgo_data
Docker volume, which persists the data even if you remove the container.
Now anyone who owns the CS:GO game can join your server by connecting via the community server browser in game
or with this URL, where example.com
is your server's domain or IP address and sv_password
is the password you set up in step 2.2 in the autoexec.cfg
:
steam://connect/<example.com>/<sv_password>
All steps are essentially the same for other source games from Valve, such as Garry's Mod or CSS, feel free to try out those games as well.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
By making a contribution to this project, I certify that:
The contribution was created in whole or in part by me and I have the right to submit it under the license indicated in the file; or
The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same license (unless I am permitted to submit under a different license), as indicated in the file; or
The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.
I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the license(s) involved.