The Counter-Strike 2 dedicated server for Flux, as
runonflux/cs2-server-flux. Valve's native 64-bit Linux server (Steam app 730, anonymous SteamCMD),
installed and updated on every start, under a supervisor (bash, PID 1) that:
- builds the launch line from the environment (
CS2_*), every value validated - runs the server in a pseudo-terminal fed by a FIFO, so its console reaches the logs and the hub
(
flux-console) - keeps the owner's files on the data volume (
cfg/, CounterStrikeSharp's plugins, configs and admins, the two ban lists) and links them into the install; everything else is Valve's, local to the node - installs and updates Metamod:Source 2.0 and CounterStrikeSharp (with its .NET runtime) when
CS2_ADDONSis on, and puts Metamod's line back intogameinfo.gion every start, because every game update writes that file away - goes into safe mode (starts without the addons) when a server with them dies twice in a row
within 3 minutes of starting, which is what a Valve update that Metamod/CounterStrikeSharp do not
support yet looks like; it tries again after an addon update, the next game update, or
flux-cs2 addons-retry - backs up the owner's files on start (unless one is younger than an hour) and every 6 hours
- quits cleanly on stop (inside Docker's 10 s: the server is ended by force at 8 s) and restarts the server in place when it ends on its own, up to 5 times an hour, then exits 42 so the platform rebuilds or moves the container
CS2 has no world to save: everything the panel changes (settings, plugins, admins, bans) is written to the data volume the moment it is made, so a stop or a kill loses nothing.
| Path | Flux mount | What |
|---|---|---|
/mnt/cs2/data |
g: (synced, small) |
cfg/ (server.cfg, flux/settings.cfg, flux/mapcycle.txt, plugin cfgs), css/ (plugins, configs, admins, gamedata), banned_user.cfg, banned_ip.cfg, flux/ (backups, addon state) |
/mnt/cs2/server |
ml:server (local, never synced) |
the game, about 69 GB, and the console logs (flux-logs/, 10 MB each, the newest 5) Also flux-logs/flux.log (our supervisor log, 5 MB then one .1): local, never synced. |
Spec: g:/mnt/cs2/data|ml:server:/mnt/cs2/server. Never put the server volume on a g: mount: Syncthing
would copy 69 GB.
GAME_PORT (27015): the game and the Steam query over UDP, RCON over TCP. SourceTV (27020) is not
served by this image.
| Variable | ||
|---|---|---|
CS2_HOSTNAME |
+hostname |
63 characters, no double quotes or semicolons |
CS2_GAME_MODE |
+game_type/+game_mode |
competitive (default), casual, wingman, deathmatch, armsrace, custom |
CS2_MAP |
+map |
a map, or a Workshop map as an ID or link (default de_dust2) |
CS2_WORKSHOP_COLLECTION |
+host_workshop_collection |
a Workshop collection's ID or link |
CS2_MAXPLAYERS |
-maxplayers |
2-64 (default 10). The owner's to raise to the game's own 64; hardware is the only limit |
CS2_PASSWORD |
+sv_password |
no spaces or quotes |
CS2_RCON_PASSWORD |
+rcon_password |
empty = RCON off |
CS2_GSLT |
+sv_setsteamaccount |
the owner's own Game Server Login Token for App ID 730 (32 hex characters) |
CS2_ADDONS |
true (default): Metamod + CounterStrikeSharp. false: off, plugins and configs are kept |
|
CS2_EXTRA_ARGS |
appended (no port) | |
SKIP_UPDATE |
true: do not ask Steam for an update on start |
Make one at https://steamcommunity.com/dev/managegameservers with App ID 730. Without one the server
still runs (it logs in anonymously, measured), but it is not in the Internet server list. A token Steam
does not accept (revoked, made for another game, used by another running server) leaves the server
disconnected from Steam: measured, status then prints steamid : [I:0:1] and "disconnected from Steam3".
flux-cs2 status reports all three as gslt.state (missing / invalid / set) and gslt.login
(anonymous / ok / failed) with a message for the panel.
flux-console <command> types one command into the server and prints the answer (status,
kickid, changelevel, css_plugins list, meta list...).
flux-cs2 prints one JSON document per call:
| Command | |
|---|---|
status |
state (updating / starting / running / restarting / stopped / needs-fix / crashed), message, map, mode, players (A2S), build, gslt, addons, last backup |
players |
the roster: user id, name, bot, SteamID, ping, state, ip (from status + the log's connect lines) |
kick <userid> [reason], ban <steamid|ip> [minutes], unban <steamid|ip> |
bans are written to the data volume at once |
change-map <map|workshop id|link> |
changelevel or host_workshop_map |
settings / set-settings '<json>' |
the Server Settings form (34 settings): definitions and values; validated, written and applied live |
files |
stock maps, Workshop maps downloaded, map cycle, addons, plugins, the catalogue, admins, bans |
set-mapcycle '["de_dust2", ...]' |
cfg/flux/mapcycle.txt and the map group mg_flux |
plugin-add <id> / plugin-install <zip> / plugin-enable <name> on|off / plugin-remove <name> |
CounterStrikeSharp plugins |
set-admins '<json>' |
css/configs/admins.json, merged |
addons-retry |
leave safe mode |
backups / restore <backup> |
needs-fix is a state the environment causes and a restart cannot cure (a GAME_PORT that is not a
port): the container waits for a redeploy instead of crash-looping.
/tmp/flux-generation holds <generation> <epoch>: a restart is over when it changes.
Only plugins that load cleanly on the current build (CounterStrikeSharp 1.0.377, game 1.41.9.1): MatchZy, Retakes (+ Instadefuse, Clutch Announce), Deathmatch, GG1 MapChooser. Retakes and Deathmatch conflict (the catalogue says so).
./tests/test-flux.sh # unit tests, no Docker
docker build -t cs2-server-flux:local .
./tests/test-image.sh # the real supervisor around a stand-in Steam and server
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:stable -x scripts/*.sh tests/*.sh tests/image/*.sh- 69 GB installed (
csgo61 GB, community addons 6.4 GB); the download is 73 GB. - Up in about 6 s on an installed volume (plus the SteamCMD check); idle (hibernating, nobody on) 1.08 GiB RAM, about 3 % of one core; 15 bots awake 1.30 GiB, about 24 % of one core.
docker stop3.5-4.5 s with six plugins loaded.
- 1.0.0: first release.
- 1.0.3: the progress line SteamCMD ends with (
unknown, progress: 0.00 (0 / 0)) is ignored. Format checked against real SteamCMD output (app 1007). - 1.0.2: SteamCMD's download progress in
flux-cs2 status(progress: phase, percent, bytes) whileupdating; timed bans survive a restart (banned_timed.jsonon the data volume, put back at each start with the minutes they have left throughcfg/flux/timedbans.cfg; listed byflux-cs2 fileswithtimed: true);flux-cs2 workshop-remove <id>deletes a downloaded Workshop map and its manifest entry (refused while it is played). - 1.0.1:
flux.logmoved off the synced data volume to/mnt/cs2/server/flux-logs/flux.log(5 MB, one.1; an old one is moved there once); start-up banner; the weekly rebuild only when it has something to apply (new script.github/ci/needs-rebuild.sh).