--- title: "How to Build ReHLDS from Source with Docker" excerpt: "Compile ReHLDS yourself in a reproducible Docker container instead of trusting a random binary, and verify the engine you built is the one your server loads." category: "installation" tags: "rehlds, compile" image: "" read_time: "9 min read" ---
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.
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)python3gitlibc6-dev-i386)Nothing else. The project vendors what it needs.
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 .
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
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
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.
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.
fatal error: sys/cdefs.h: No such file or directory — libc6-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.--recursive. Run git submodule update --init --recursive.file engine_i486.so reports ELF 32-bit, Intel 80386.ldd resolves every dependency.rehlds_version answers.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.