You run docker compose up for your CS 1.6 server and the container immediately exits — docker ps shows nothing, docker ps -a shows it Exited (1) or Exited (0) seconds after start. A container lives only as long as its foreground process, so an HLDS container that dies instantly means hlds_run either crashed on startup or ran, printed, and returned. Here is how to find which and fix it.
1. Read the actual exit reason first
Do not guess — get the container's own output:
docker compose logs --tail=50 hlds docker ps -a
The last lines before exit are the real error. An Exited (0) usually means the process ran to completion (missing map, backgrounded); a non-zero code means a crash or a failed exec. Everything below is about interpreting those lines.
2. Provide the 32-bit runtime
HLDS and ReHLDS binaries are 32-bit. On a 64-bit base image without i386 libraries, the loader cannot even start the binary and the container dies instantly with something like:
./hlds_linux: No such file or directory
— a misleading message that actually means the 32-bit loader is missing. In your Dockerfile (Debian/Ubuntu base) enable i386 and install the libs:
RUN dpkg --add-architecture i386 \ && apt-get update \ && apt-get install -y libc6:i386 libstdc++6:i386 lib32gcc-s1
If it then complains specifically about libstdc++.so.6 or libm.so.6, see the shared-library fix — same root cause, missing 32-bit dependency.
3. Run in the foreground, not the launcher's background mode
The default hlds_run wrapper monitors and restarts the real binary, which can confuse Docker's single-process model. Point the container's command at a start that stays in the foreground and does not detach. A working compose command line is explicit:
command: >- ./hlds_run -game cstrike -console -nomaster +ip 0.0.0.0 +port 27015 +map de_dust2 +maxplayers 20
Two flags matter for containers: -console keeps output on stdout (so docker logs sees it and the process does not expect a tty), and a valid +map is mandatory. Without +map, HLDS prints Host_Error: Couldn't spawn server or exits cleanly having nothing to run.
4. Give it a real map and maxplayers
A container that exits 0 with no crash almost always lacks a startup map, or was told to run a launcher that forks and returns. The engine needs +map <name> pointing at a BSP that exists in cstrike/maps/, plus a sane +maxplayers. Confirm the map is actually in the image:
docker compose run --rm hlds ls cstrike/maps/de_dust2.bsp
If that path is missing, your volume mount or COPY left the game files out — the binary starts, finds no map, and returns.
5. Keep it attached with tty when needed
Some builds of the launcher expect an interactive terminal and exit when they do not get one. In compose, grant a pseudo-TTY and keep stdin open:
tty: true stdin_open: true restart: unless-stopped
restart: unless-stopped also stops a transient failure from leaving the container dead, but do not use it to paper over a real crash — fix the underlying error first or you get a container that restart-loops forever.
A useful debugging trick is to bypass the entrypoint entirely and get a shell inside the image, so you can run the binary by hand and read its raw output:
docker compose run --rm --entrypoint bash hlds # then, inside: ./hlds_run -game cstrike -console +map de_dust2 +maxplayers 20
Running it interactively shows the crash line the daemon would otherwise swallow, which is far faster than reading exit codes and guessing.
Common errors
./hlds_linux: No such file or directoryon a file that exists — missing 32-bit loader. Installlibc6:i386and friends.- Exited (0) immediately — no
+map, or the map BSP is not in the image. Add a valid startup map. Host_Error: Couldn't spawn server— bad or missing map name.- Container runs but nothing in logs — missing
-console; output is going to a tty the daemon cannot read. - Restart-loops forever —
restart: unless-stoppedmasking a startup crash. Readdocker compose logsand fix the real error.
Verification
Start it and confirm the process stays up:
docker compose up -d hlds docker ps docker compose logs --tail=20 hlds
docker ps should list the container as Up, and the logs should end with the server reaching a map and printing its ready banner rather than a Host_Error. Query the port from the host to confirm it answers, then connect a client. If it stays up but no one can join, that is a networking/port-forward issue, not a startup crash — a different problem from the one this fixes.









