How to Build ReHLDS from Source with Docker

August 13, 2025 Daemon666 9 min read 13 преглеждания

There are two honest reasons to build ReHLDS yourself. The first is trust: you are about to run a network-facing binary as a long-lived process, and "some .so from a forum post" is not a supply chain. The second is that you want a specific commit — a fix that is merged but not yet in a tagged release. Docker makes both reproducible, because the whole 32-bit toolchain lives in a throwaway container instead of on your host.

1. What ReHLDS actually needs to build

ReHLDS is a 32-bit C++ project. On Linux it builds with a 32-bit GCC/Clang toolchain, and it uses a Python-based build script. So the container needs:

  • gcc-multilib / g++-multilib (or a Clang with 32-bit support)
  • python3
  • git
  • the 32-bit development libraries (libc6-dev-i386)

Nothing else. The project vendors what it needs.

2. A build container

Write Dockerfile.rehlds:

FROM debian:12

RUN dpkg --add-architecture i386 \
 && apt-get update \
 && apt-get install -y --no-install-recommends \
      build-essential gcc-multilib g++-multilib libc6-dev-i386 \
      python3 python3-pip git ca-certificates \
 && rm -rf /var/lib/apt/lists/*

WORKDIR /src
CMD ["/bin/bash"]

Build it:

docker build -t rehlds-build -f Dockerfile.rehlds .

3. Clone and check out a known commit

Do this on the host so the source survives the container:

mkdir -p ~/build && cd ~/build
git clone --recursive https://github.com/rehlds/rehlds.git
cd rehlds
git log --oneline -5

Use --recursive. ReHLDS pulls in submodules; a clone without them fails partway through the build with missing-header errors that look like a broken toolchain but are not. If you already cloned without it:

git submodule update --init --recursive

Pin what you build. Either check out the tag matching the latest release, or note the commit hash — an unpinned main is not reproducible and you will not be able to tell, six months later, what is actually on your server.

git checkout <tag-or-commit>
git rev-parse HEAD > ../rehlds-commit.txt

4. Build inside the container

docker run --rm -it \
  -v ~/build/rehlds:/src \
  -w /src \
  rehlds-build bash

Inside the container, invoke the project's build script. ReHLDS uses a Python build wrapper at the repository root; run it with the release configuration:

python3 build.py --clean

If the script name or flags differ in the revision you checked out, read the repository's own build documentation rather than guessing — the project has changed build systems more than once over its life, and inventing flags will only waste your afternoon.

On success the artefacts land in a build/ or publish/ directory. The one file you care about on Linux is:

engine_i486.so

5. Sanity-check the artefact before you ship it

A 64-bit build is the single most common outcome of a misconfigured multilib toolchain, and it fails at load time with a confusing error. Check the architecture explicitly:

file engine_i486.so

It must report:

ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked

If it says ELF 64-bit ... x86-64, the multilib packages were not actually used. Rebuild with the 32-bit flags forced, and do not deploy the 64-bit object — it will produce wrong ELF class: ELFCLASS64 at startup (see that error).

Check the dynamic dependencies too:

ldd engine_i486.so

Every entry must resolve. A not found line here means the target server will need that 32-bit library installed — see the 32-bit library guide.

6. Deploy

cd /home/steam/hlds
cp engine_i486.so engine_i486.so.bak
scp user@buildhost:~/build/rehlds/build/engine_i486.so ./engine_i486.so
chmod 644 engine_i486.so
./hlds_run -game cstrike +map de_dust2 +maxplayers 20 +sv_lan 0

Then, in the console:

rehlds_version

It should print the version corresponding to what you built. Record the commit hash from rehlds-commit.txt next to the deployed binary — a year from now, "which ReHLDS is this?" is a question you will actually need to answer.

Common errors

  • fatal error: sys/cdefs.h: No such file or directorylibc6-dev-i386 is missing from the container.
  • skipping incompatible /usr/lib/x86_64-linux-gnu/... when searching for -lstdc++ — the linker is finding 64-bit libraries for a 32-bit link. g++-multilib is not installed, or -m32 is not being passed.
  • Missing headers from a submodule path — you cloned without --recursive. Run git submodule update --init --recursive.
  • Builds fine, server segfaults on start — you built for a different ABI than the rest of your stack, or mixed a self-built engine with a much older Metamod. Test on a staging server, never straight onto the public one.

Verification

  1. file engine_i486.so reports ELF 32-bit, Intel 80386.
  2. ldd resolves every dependency.
  3. The server boots and rehlds_version answers.
  4. A client connects and plays for a full map without a crash.

Once that holds, wire it into your normal deploy. The same container pattern works for compiling ReGameDLL_CS, which is the natural next step — the two are versioned together and you want them from the same era.

Сътрудници: Daemon666 ✦
Сподели: