Skip to main content

Building on Two Machines

How to use Unreal Build Accelerator to speed up C++ compilation by using two machines.

Setup

Unreal uses two distinct technologies, namely Unreal Build Accelerator (UBA) and Horde.

UBA is a tool which speeds up builds by using file caching and by distributing C++ compilation over multiple machines.

Horde is a distributed build system. It uses UBA to distribute C++ compilation tasks, and in addition can also do shader compiling, packaging, and build scheduling.

In this example we have two machines:

NameHorde RoleFunction
R5ClientThe main development machine
R9Client & ServerSecondary machine which will do C++ compilation tasks when a build is run on the main machine

Unreal Engine and Visual Studio are installed on both machines.

Building and installing the Horde server

The Horde server is not shipped with Unreal; you need to build it.

The Horde Installation Tutorial describes how to install and configure Horde but it is out of date. It describes using the file Engine\Extras\Horde\UnrealHordeServer.msi which does not ship with the engine (at least in 5.5.4). It needs to be built, which needs a source build. If you don't have a source build, see here for details on downloading and building one.

Using a source build:

  • In Visual Studio open Engine\Source\Programs\Horde\Horde.sln

  • build the solution

  • on the machine which is the Horde server, run Engine\Extras\Horde\UnrealHordeServer.msi

  • Use the default options as shown:

width50

Installing the Horde Agents

On the server machine

On the server machine, open the Horde server in a browser at (change SERVERNAME to your own server's name)

http://SERVERNAME:13340

Use the Tools | Downloads menu to display a list of downloads, click Horde Agent (Windows Installer) and download and run the UnrealHordeAgent.msi file.

Accept all the default options

Once you have pressed the Finish button the agent will try and register with the server, after a small delay you will see this confirmation screen:

Click the Register button.

On the client machine

On the client machine, open the Horde server in a browser at (change SERVERNAME to your own server's name)

http://SERVERNAME:13340

Use the Tools | Downloads menu to display a list of downloads, click Horde Agent (Windows Installer) and download and run the UnrealHordeAgent.msi file.

Change the server url to the name of the server machine:

Once you have pressed the Finish button the agent will try and register with the server, after a small delay you will see this confirmation screen:

Click the Register button.

Once the non-server machine is registered with the server you should see both machines registered:

Configuring your project

The build process looks in various places for the file BuildConfiguration.xml.

This article describes some locations where the BuildConfiguration.xml can be located, and the source file Engine\Source\Programs\UnrealBuildTool\System\XmlConfig.cs describes a few more places:

DescriptionLocationCondition
"Engine (NotForLicensees)"Engine/Programs/NotForLicensees/UnrealBuildToolOnly when using an engine source build
"Engine (Saved)"Engine/Saved/UnrealBuildToolOnly when using an engine source build
"Global (ProgramData)"c:\ProgramData\Unreal Engine\UnrealBuildTool
"Global (AppData)"c:\user\$USER\AppData\Roaming\Unreal Engine\UnrealBuildTool
"Global (LocalAppData)"c:\user\$USER\LocalAppData\Unreal Engine\UnrealBuildTool
"Global (Documents)"c:\user\$USER\My Documents/Unreal Engine/UnrealBuildTool

If more than one file exists, they will all be read, and listed in the log like this:

Log started at 05/14/2025 19:21:22 (2025-05-14T07:21:22Z)
No config file at C:\Users\jfarrow\AppData\Local\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml
No config file at C:\Users\jfarrow\Documents\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml
Configuration will be read from:
C:\ProgramData\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml
C:\Users\jfarrow\AppData\Roaming\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml

It seems that the location Engine/Saved/UnrealBuildTool, which is only when using an engine source build, is only read when doing a build from Visual Studio, not when using the UnrealBuildTool.exe on the command line. This is confirmed using this filter in Process Monitor

Choose one of these locations for BuildConfiguration.xml and add these elements to enable UBA:

<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowUBAExecutor>true</bAllowUBAExecutor>
<bPrintDebugInfo>true</bPrintDebugInfo>
</BuildConfiguration>
<Horde>
<Server>http://R9:13340</Server>
<WindowsPool>Win-UE5</WindowsPool>
</Horde>

<UnrealBuildAccelerator>
<bLaunchVisualizer>false</bLaunchVisualizer>
<bForceBuildAllRemote>false</bForceBuildAllRemote>
</UnrealBuildAccelerator>

</Configuration>

Testing from the Command Line

To test the build process on the client machine change into your project directory and run the unreal build tool. Here I am using the Lyra starter game example (in a directory called "d:\work\LyraStarterGame554") and building the LyraEditor:

cd d:\work\LyraStarterGame554

"c:\program files\epic games\UE_5.5\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe" -Project="d:\work\LyraStarterGame554\LyraStarterGame.uproject" LyraEditor Win64 Development -Rebuild -UBA -UBAVisualizer

This will build the project using both machines. After a few seconds the UBA Visualizer window will appear showing files being compiled on each machine like this:

Testing from Visual Studio

Provided UBA is enabled in the BuildConfiguration.xml file as described above building inside Visual Studio will use UBA without any changes to the project files.

Not Enough Work

If there is not enough work to justify the overhead of using the Horde server and UBA they will not be used.

If we configure the LyraStarterGame project to do non-unity builds, the build process creates 538 object files.

If we delete 2 of them, the build process only has 5 actions (including 2 compiles, 2 links) and the log looks like this:

d:\work\LyraStarterGame554>"c:\program files\epic games\UE_5.5\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe" -Project="d:\work\LyraStarterGame554\LyraStarterGame.uproject" LyraEditor Win64 Development -UBA
Log file: C:\Users\jfarrow\AppData\Local\UnrealBuildTool\Log.txt
Using 'git status' to determine working set for adaptive non-unity build (D:\work\LyraStarterGame554). Building LyraEditor...
Using Visual Studio 2022 14.38.33144 toolchain (C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130) and Windows 10.0.22621.0 SDK (C:\Program Files (x86)\Windows Kits\10).
[Adaptive Build] Excluded from LyraGame unity file: InventoryFragment_ReticleConfig.cpp, LyraDamageLogDebuggerComponent.cpp, LyraGameplayAbility_RangedWeapon.cpp, LyraRangedWeaponInstance.cpp, LyraWeaponDebugSettings.cpp, LyraWeaponInstance.cpp, LyraWeaponSpawner.cpp, LyraWeaponStateComponent.cpp
Determining max actions to execute in parallel (16 physical cores, 32 logical cores)
Executing up to 16 processes, one per physical core
Using Unreal Build Accelerator executor to run 5 action(s)
UbaSessionServer - Disable remote execution (remote sessions will finish current processes) ------ Building 5 action(s) started ------ [1/5] Compile [x64] LyraEditorEngine.cpp [2/5] Compile [x64] EditorValidator.cpp [3/5] Link [x64] UnrealEditor-LyraEditor.lib [4/5] Link [x64] UnrealEditor-LyraEditor.dll Creating object D:\work\LyraStarterGame554\Binaries\Win64\UnrealEditor-LyraEditor.exp [5/5] WriteMetadata LyraEditor.target (UBA disabled)

Up to around 50 actions UBA will not be used to compile files on agent machines - all the work will be done on the machine which started the compile process.

Logging and Possible problems

When you compile from the command line one of the first lines displayed tells you where the log file goes, like this:

Log file: C:\Users\jfarrow\AppData\Local\UnrealBuildTool\Log.txt

Note the log file is only copied to this location when the UnrealBuildTool finishes.

In this file you should see lines like this, showing files being compiled on both machines:

[262/421] Compile [x64] GameSetting.cpp [RemoteExecutor: R9]
[263/421] Compile [x64] Module.UIExtension.cpp [RemoteExecutor: R5]
[264/421] Compile [x64] CommonUserBasicPresence.cpp [RemoteExecutor: R9]
[265/421] Compile [x64] WhenPlayingAsPrimaryPlayer.cpp [RemoteExecutor: R9]
[266/421] Compile [x64] AsyncMixin.cpp [RemoteExecutor: R9]
[267/421] Compile [x64] GameResponsivePanel.cpp [RemoteExecutor: R9]
[268/421] Compile [x64] LyraHealExecution.cpp [RemoteExecutor: R5]

Minor Messages

This message:

Horde URL: (null), Pool: (none), Cluster (none), Condition: (none), Connection: (none) HordeEncryption: (none)

can be ignored, everything still works when this is displayed. Curiously when compiling under Visual Studio the details are filled in:

Horde URL: http://r9:13340/, Pool: Win-UE5, Cluster (none), Condition: (none), Connection: (none) HordeEncryption: (none)

No additional agents available

This message appears when everything is working correctly.

UBASessionServer

This can generate a number of different messages including:

UbaSessionServer - Allocation failed in MapViewOfFile (The paging file is too small for this operation to complete.).. process will sleep and try again

[Worker0] UbaSessionClient - Allocation failed in VirtualAlloc (The paging file is too small for this operation to complete.).. process will sleep and try again

cl : Command line error D8027 : cannot execute 'C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\Hostx64\x64\c1xx.dll' Compile [x64] LyraCharacter.cpp: Exited with error code 2 . The build will fail. Compile [x64] LyraCharacter.cpp: WorkingDirectory C:\Program Files\Epic Games\UE_5.5\Engine\Source Compile [x64] LyraCharacter.cpp: C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\Hostx64\x64\cl.exe @"D:\work\LyraStarterGame554\Intermediate\Build\Win64\x64\UnrealEditor\Development\LyraGame\LyraCharacter.cpp.obj.rsp"

This "The paging file is too small" can be fixed by going into Settings | Advanced System Settings | Performance Setting | Advanced, and change the virtual memory setting to a large value:

width5010

Changing to 128000 MB allocated on each SSD drive fixed the problem.

Disable remote execution / Got remote execution disabled

UbaSessionServer - Disable remote execution (remote sessions will finish current processes)
UbaSessionServer - Disable remote execution on R5 because remote execution has been disabled and queue is empty (will finish 31 processes)
[Worker0] UbaSessionClient - Got remote execution disabled response from host (will finish 32 active processes)
UbaSessionServer - Disable remote execution on R9 because remote execution has been disabled and queue is empty (will finish 31 processes)

These messages occur when everything is working properly.

I think the message "Disable remote execution on R9" happens because the R9 machine is faster and has finished its allocated tasks before the main machine.

Compiling does not work on the server machine

When you run the command line compile the compilation task communicates with the Horde server specified in the Saved/UnrealBuildTool/BuildConfiguration.xml file.

The Horde server replies with details of the names or IP addresses of the clients and the ports to used communicating with them.

If everything is working, you will see something in the log like this:

[Worker0] Connecting to R5 at 10.0.0.13:7000 (Direct via None) with nonce 7b148ff6f35b23930b0de56a80c92245f968668e30a9043f36d6223eefc377f148e2cc5eef3cadf37b59c397aded48a6716d2a919884a078825c3259315ddee2 and encryption None...
[Worker0] Connected to R5 (10.0.0.13) under lease 6822c0c99d70d26cc3169fa2 (agent version: 5.5.0-37571337)
[Worker0] Agent properties:
[Worker0] Sending ping message
[Worker0] Running worker task..
[Worker0] Attaching recv buffer 0
[Worker0] Attaching send buffer 0
[Worker0] Started socket reader
[Worker0] Waiting for attach...
[Worker0] Uploading files...
[Worker0] Executing child process: UbaAgent.exe -Listen=7001 -ListenTimeout=10 -NoPoll -Quiet -ProxyPort=7002 -Dir=%UE_HORDE_SHARED_DIR%\Uba -Eventfile=%UE_HORDE_TERMINATION_SIGNAL_FILE% -MaxIdle=15
[Worker0] UbaAgent v5.6.0-Uba_v1.0.0-36290034 (Cpu: 32, MaxCon: 4, Dir: "C:\HordeAgent\Sandbox\Saved\Uba", StoreCapacity: 20Gb)
[Worker0] Will poll for external events in file C:\HordeAgent\Sandbox.horde-termination-signal
[Worker0] UbaClient (c1a05e2e-e993-4d35-9a66-e891a5cfdb34) - Listening on 0.0.0.0:7001
[Worker0] Connecting to UbaAgent on 10.0.0.13:7001 (local agent port 7001) 0.218 seconds after assigned UbaServer - Connected to 10.0.0.13:22811 (bbcf7155-f938-4f68-96e2-80c44868cba5)
[Worker0] UbaClient (c1a05e2e-e993-4d35-9a66-e891a5cfdb34) - Connected to server... (0x0000034320010150)
[Worker0] UbaStorageClient - Database loaded from C:\HordeAgent\Sandbox\Saved\Uba\cas\casdb (v32) in 8ms (contained 62385 entries estimated to 19.9gb)
[Worker0] Client session 250513_154720 started

You will see this for each worker (i.e. for each Horde client), with the lines intermingled like this:

[Worker0] Connecting to R5 at 10.0.0.13:7000 (Direct via None) with nonce 7b148ff6f35b23930b0de56a80c92245f968668e30a9043f36d6223eefc377f148e2cc5eef3cadf37b59c397aded48a6716d2a919884a078825c3259315ddee2 and encryption None...
[Worker1] Connecting to R9 at 10.0.0.21:7000 (Direct via None) with nonce d5373895eb3fbf91e3b88f70e6490a52d2a6d710d1f8fd88114263669498682b73388ee2a7b0296c18f4a0ea51328ce1994e9d7efa031a6ade1864e710a2eddc and encryption None...
[Worker0] Connected to R5 (10.0.0.13) under lease 6822c0c99d70d26cc3169fa2 (agent version: 5.5.0-37571337)
[Worker1] Connected to R9 (10.0.0.21) under lease 6822c0c99d70d26cc3169fa9 (agent version: 5.5.0-37571337)

The key point here is that the Horde server can send a name or an IP address of a Horde agent for the compiling machine to use to talk to that agent. If the server sends a value like "localhost:7000" or "127.0.0.1:7000" this refers to the machine it is used on, so when the compiling machine uses it, it won't work, because instead of referring to the agent machine it refers to the compiling machine. If this happens you see something like this:

[Worker1] Connecting to R9 at 127.0.0.1:7000 (Direct via None) with nonce a917a8f29ec9639a1ee0e5eb85aa2d5951af21a88f05cfc8aca2e51d298ef9b01aae870de9a541ea203aada7d704d6a514626549f6abbeca606dec00e46df9b0 and encryption None...
[Worker1] Connected to R9 (127.0.0.1) under lease 682273cc503cd4f93df4932b (agent version: 5.5.0-37571337)
[Worker1] Agent properties:
[Worker1] Sending ping message
[Worker1] Running worker task.. [Worker1] Attaching recv buffer 0
[Worker1] Attaching send buffer 0
[Worker1] Waiting for attach... [Worker1] Started socket reader
Unable to assign a remote worker

This problem can be confirmed by looking at the Horde server web interface. If you select Server | Agents from the menu, right click the agent in the agent list and select "Audit", then in the Agent Audit screen click "View Agent" on the right you see this screen:

This shows a ComputeIp address of "127.0.0.1", which is not going to work.

You can fix this by editing the file C:\ProgramData\Epic\Horde\Agent\agent.json and specifying the agent IP like so:

{
"Horde": {
"ComputeIp": "10.0.0.21"
}
}

After this change restart the HordeAgent service (can be done from Task Manager).

Alternatively, you can restart it from a command window run with administrator access:

net stop HordeAgent
net start HordeAgent

Horde log files

Additional Horde log files can be found in:

  • c:\ProgramData\Epic\Horde\Agent\Log*.json
  • c:\ProgramData\Epic\Horde\Server\Log*.json

Registry Settings

On an Agent machine, the address of the Horde server is stored in the registry:

Visual Studio

Once you have confirmed everything is running from the command line you should be able to build from Visual Studio without making any changes to the project.

Sample Timings

Compiling the Lyra Editor in the sample Lyra project means compiling 421 files.

The compile command for this is:

"c:\program files\epic games\UE_5.5\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe" -Project="d:\work\LyraStarterGame554\LyraStarterGame.uproject" LyraEditor Win64 Development -Rebuild -UBA

These tests were run on two machines with 64 GB RAM each using M2 drives. Machines are connected on a 10 GB LAN. These are some timings using individual machines and using both machines together:

Compiling MachineAdditional UBA MachineCompile Time (seconds)
Ryzen 5950x104
Ryzen 9950x65
Ryzen 5950xRyzen 9950x67
Ryzen 9950xRyzen 5950x46

So clearly UBA does make the build significantly faster.

One consideration is the time before compiling actually starts. Looking at this screenshot from the UBA Visualizer on the Ryzen 5950x, we can see it takes nearly 30 seconds before the compiling actually starts:

On the faster Ryzen 9950x that startup time is reduced to 20 seconds:

Feedback

Please leave any feedback about this article here