Offloading AI with Arma 3 Headless Clients via LinuxGSM
The Real Virtuality engine that powers Arma 3 is fundamentally single threaded for AI calculations, which means a mission with 200 AI units will drop the main server thread to single digit FPS even on a fast modern CPU. The fix is the headless client: a stripped down arma3server instance that connects to the main server as a player slot, accepts ownership of AI groups via scripting, and runs all of their pathfinding and behavior on its own CPU core.
This guide stands up the main Arma 3 server with LinuxGSM, spins up two headless clients on the same host, whitelists their loopback IP in server.cfg, and adds an init script that hands AI groups off to whichever headless client is least loaded.
Prerequisites
- Ubuntu 24.04 LTS with at least 16 GB RAM and 8 modern CPU cores.
- A non root user (LinuxGSM refuses to run as root).
- UDP 2302 through 2306 open at the firewall.
- A purchased Arma 3 license is not required for the dedicated server, but mods must be downloaded with a Steam account that owns the game.
Step 1: Install the Main Server
sudo adduser --disabled-password --gecos "" arma3sudo machinectl shell arma3@cd ~wget -O linuxgsm.sh https://linuxgsm.shchmod +x linuxgsm.shbash linuxgsm.sh arma3server./arma3server installStep 2: Install the Headless Client Binary
The headless client is shipped as a separate instance of LinuxGSM, sharing the same SteamCMD install. We create two instances, listening on loopback only.
./linuxgsm.sh arma3server-hc1./linuxgsm.sh arma3server-hc2./arma3server-hc1 install./arma3server-hc2 installStep 3: Whitelist the Local Client IP
Without an entry in headlessClients[], the main server treats the connecting HC as a normal player, which means it counts against the slot limit and cannot receive script ownership.
headlessClients[] = {"127.0.0.1"};localClient[] = {"127.0.0.1"};battleyeLicense = 1;verifySignatures = 2;kickDuplicate = 1;Step 4: Headless Client Startup Parameters
startparameters="-client -connect=127.0.0.1 -port=2302 \ -password=YOUR_SERVER_PASSWORD \ -profiles=/home/arma3/hc1 -name=hc1 \ -nosound -mod=@CBA_A3;@ace"Repeat with hc2 in the second instance file, pointing at the same port. The main server will accept both because both originate from the loopback address listed in headlessClients[].
Step 5: Hand Off AI Groups in the Mission Init
if (!isServer) exitWith {};_hcList = entities "HeadlessClient_F";_idx = 0;{ if (side _x == east) then { _hc = _hcList select (_idx mod (count _hcList)); _x setGroupOwner (owner _hc); _idx = _idx + 1; };} forEach allGroups;Performance and Tuning
- Pin each headless client to its own CPU set with
tasksetso the kernel scheduler does not bounce them across cores. - Cap each HC at one CPU core: AI logic is single threaded, more cores per HC are wasted.
- Tune
MaxMsgSendinbasic.cfgupward (1024) so AI ownership transfer packets are not dropped.
Conclusion
A correctly configured headless client architecture moves Arma 3 from missions that visibly stutter at 80 AI to missions that hold a steady framerate at 400 AI. Whitelist the loopback IP, pin each HC to its own core, and distribute group ownership in the mission init.