Skip to content

IronOak-Studios/BrainBread-Docker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BrainBread-Docker

Dockerized BrainBread dedicated server running on Half-Life Dedicated Server (HLDS). The Docker image provides the engine (downloaded via SteamCMD at build time), while the BrainBread mod files are bind-mounted at runtime from a local brainbread/ directory.

Prerequisites

  • Docker and Docker Compose
  • A populated brainbread/ directory containing the server mod files (see below)

Setting up the brainbread/ directory

The brainbread/ directory is gitignored and must be populated before the first run. It should contain the full server-side mod data: maps, models, sounds, sprites, configs, and the server DLL (dlls/bb.so).

From a server package (recommended)

Download the latest Linux server package from ironoak.ch/BB and extract it in the repository root:

tar xzf brainbread-v1.3.37-linuxserver.tar.gz

This produces a brainbread/ directory with everything the server needs, ready to go.

From the data repository

Alternatively, clone the BrainBread data repository directly:

git clone https://github.com/IronOak-Studios/BrainBread.git brainbread

The repository includes the server DLL, maps, and all mod data.

Building the image

docker compose build

Or without Compose:

docker build -t brainbread .

The build is a two-stage Dockerfile that:

  1. Downloads HLDS (app 90) via SteamCMD
  2. Compiles stat_fix.so and healthcheck (see Technical notes)
  3. Produces a minimal final image with just the HLDS runtime and helper binaries

The HLDS download is the slow step (~1.5 GB). Docker layer caching means subsequent rebuilds (e.g. after editing stat_fix.c or entrypoint.sh) skip the download.

Running the server

docker compose up -d

Or without Compose:

docker run -d \
    -p 27015:27015/udp \
    -p 27015:27015/tcp \
    -v ./brainbread:/opt/hlds/brainbread \
    brainbread

Environment variables

All variables are optional and have sensible defaults. Set them in docker-compose.yml under environment: or pass them with docker run -e.

Variable Default Description
SERVER_NAME BrainBread v1.3.37 Server Hostname shown in the server browser
MAP bb_chp1_heavensgate Starting map
MAXPLAYERS 12 Player slots (2-32)
RCON_PASSWORD (empty -- RCON disabled) Remote console password
SERVER_PORT 27015 Listen port
EXTRA_ARGS (empty) Additional hlds_linux arguments

These are passed as command-line +args to hlds_linux by the entrypoint script, and override matching values in server.cfg.

Configuration

Server configuration lives in the bind-mounted brainbread/ directory. The main files to edit:

  • brainbread/server.cfg -- Server cvars (hostname, timelimit, difficulty, experience settings, etc.). Executed on every map change.
  • brainbread/mapcycle.txt -- Map rotation list.

Changes to these files take effect on the next map change or server restart -- no image rebuild required.

Updating the mod

The Docker image only contains HLDS and the stat shim -- it does not need rebuilding for mod updates.

To update just the server DLL:

cp bb.so brainbread/dlls/
docker compose restart

For a full mod update, extract a new server package over the existing directory. This overwrites binaries and mod assets but preserves any custom configs (like server.cfg) that aren't in the archive:

tar xzf brainbread-v1.4-linuxserver.tar.gz
docker compose restart

If you set up via git clone, pull the latest changes instead:

git -C brainbread pull
docker compose restart

Auto-restart and watchdog

The entrypoint runs hlds_linux inside a restart loop. If the server process crashes, it is restarted automatically after a 3-second delay.

A background watchdog probes the server every 60 seconds with a UDP A2A_PING query (the standard GoldSrc server ping). If the server fails to respond 3 times in a row (i.e. unresponsive for ~3 minutes), the watchdog kills the process, triggering a restart. The watchdog waits 120 seconds after each start before probing to allow time for map loading.

docker compose stop and docker stop send SIGTERM, which the entrypoint traps to shut down the server gracefully and exit the restart loop.

Technical notes

stat_fix.so

HLDS is a 32-bit application. When it calls stat() or readdir() on files living on a filesystem with 64-bit inodes (overlayfs, bind mounts, tmpfs -- common in containers), glibc returns EOVERFLOW because the inode number doesn't fit in the 32-bit struct stat.

stat_fix.c compiles into a small shared library that intercepts __xstat, __lxstat, and readdir, routing them through their 64-bit counterparts (stat64, lstat64, readdir64) and truncating the results back to 32-bit structs. The entrypoint script preloads it via the 32-bit dynamic linker (ld-linux.so.2 --preload).

healthcheck

healthcheck.c compiles into a small 32-bit binary that sends a UDP A2A_PING packet (0xFFFFFFFF69) to localhost on a given port and waits for the server's A2A_ACK response. It exits 0 on success, 1 on timeout. The watchdog in entrypoint.sh uses this to detect a hung server without requiring any runtime dependencies beyond the binary itself.

Multi-stage build

The Dockerfile uses two stages to keep the final image small. The builder stage pulls in gcc-multilib, curl, and SteamCMD -- none of which are needed at runtime. Only HLDS, the compiled shim, and minimal 32-bit runtime libraries are copied into the final image.

Releases

No releases published

Packages

 
 
 

Contributors