Run Unreal Engine's Multi-User Editing server as a persistent, hardened Docker container on your own machine or VPS — instead of requiring every collaborator to be on the same LAN, or manually running a server from someone's own Editor session each time.
If you've ever wanted a stable, always-on Multi-User server that your whole team can just connect to, without dealing with UDP/multicast/LAN quirks every session, this is that setup.
Multi-User Editing (internally called Concert) is Epic's real-time collaborative editing system — multiple people editing the same level at once, seeing each other's changes live. Epic's own docs: Multi-User Editing.
It works over UDP Messaging — a lightweight, connectionless network
protocol with no built-in authentication of its own. On a LAN, clients
find each other via multicast auto-discovery. That doesn't work over a
VPN or the open internet, so instead each client is configured with a
Static Endpoint — the server's exact IP:port, baked into the
project's Config/DefaultEngine.ini — pointing directly at it.
The out-of-the-box options for a remote team are:
- Someone launches Multi-User from their own Editor each session, and everyone else connects to their machine. No persistence — if the host closes their Editor, the session (and any unsaved changes) is gone. And reaching that machine still means either a VPN or exposing a UDP port on someone's home router directly to the internet.
- Run a dedicated Multi-User server, but leave it open on a public IP with no authentication — because Concert has none built in.
This repo does the alternative: a dedicated, always-on server, reachable only over a private mesh VPN (built for Tailscale, though any VPN that gives you a stable private IP works the same way), so there's no public exposure of the Multi-User port at all — the VPN membership is the entire access control.
- The server binary is compiled from Unreal Engine's own source for Linux, stripped down to just the Concert/UDP Messaging/SQLite plugins it needs. It doesn't need the Editor, Content, or rendering — it only relays messages between connected clients.
- It runs in Docker with
network_mode: host, because Concert needs to see its own real IP address (for the endpoint mechanism above) — Docker's normal bridge networking would hide that behind an internal IP the clients could never reach. - The container binds to a specific IP you configure (your
VPN/Tailscale address), not
0.0.0.0— so even if your host firewall were ever misconfigured, the socket itself was never opened on a public interface in the first place. Defense in depth, not just a firewall rule. - Hardened beyond Docker defaults: runs as a non-root user, all Linux capabilities dropped, read-only root filesystem (only the session data directories are writable).
- The entrypoint can optionally wait for your VPN interface to come up before starting the server, so it doesn't crash-loop if the container starts before your VPN client connects.
Concert itself has no login, password, or per-user authentication. VPN membership is the entire access boundary. Anyone on your tailnet/VPN who has a matching, synced copy of the project can join a session. If you need to revoke someone's access, you do it at the VPN layer (e.g. Tailscale's admin console), not here.
- An Epic Games account linked to GitHub
and accepted into the
EpicGamesGitHub org — required to clone engine source at all. - Windows, with Visual Studio 2022 Build Tools (C++ workload) and Epic's Linux cross-compile toolchain installed — this is how you cross-compile the Linux server binary. ~100GB+ free disk space for the engine source checkout.
- A Linux host with Docker + Docker Compose to actually run the container.
- A VPN/mesh network (Tailscale, or similar) connecting your server and every collaborator's machine.
1. Build the server binary (on Windows):
.\build.ps1 -Branch 5.8 -ToolchainRoot "D:\UnrealToolchains\v26_clang-20.1.8-rockylinux8"This clones the matching engine branch, builds UnrealMultiUserServer
for Linux, and stages a minimal Engine/ folder under .\dist. See
.\build.ps1 -? for all parameters (skip the clone if you've already
got a checkout, change the output directory, etc).
2. Configure:
copy .env.example .env
# edit .env: set BIND_IP to your server's VPN/Tailscale addressAlso edit dist\Engine\Programs\UnrealMultiUserServer\Config\DefaultEngine.ini
and set ServerName to whatever you want collaborators to see in the
Multi-User Browser.
3. Deploy:
Copy dist\Engine, Dockerfile, entrypoint.sh, docker-compose.yml,
and .env to your Linux host (same relative layout), then:
docker compose up -d --build4. Point your project at it. In your Unreal project's
Config/DefaultEngine.ini (checked into source control, so nobody has
to configure this by hand):
[/Script/UdpMessaging.UdpMessagingSettings]
EnableTransport=True
UnicastEndpoint=0.0.0.0:0
+StaticEndpoints=<BIND_IP>:7000Collaborators join via Window → Developer Tools → Multi-User Browser in the Editor, once they're connected to the VPN.
All set via .env (copy from .env.example):
| Variable | Default | Description |
|---|---|---|
BIND_IP |
(required) | The IP the server binds to and advertises — your VPN/Tailscale address. |
MU_PORT |
7000 |
UDP port for the Multi-User transport. |
WAIT_IFACE |
tailscale0 |
Network interface to wait for before starting (blank to skip). |
UE_VERSION |
latest |
Image tag to build/run — see below for running multiple engine versions side by side. |
MEM_LIMIT |
512m |
Container memory cap. |
CPU_LIMIT |
1.5 |
Container CPU cap. |
Everyone connecting to a Multi-User session must be on the same engine version — Concert doesn't support mixed-version sessions, and the wire format can differ between versions even within the same major release. When your team upgrades, you need a matching server.
You don't have to take the old one down first. Build the new version into its own tagged image, and cut over when you're ready:
.\build.ps1 -Branch 5.8 -ToolchainRoot ... -OutputDir .\dist-5.8UE_VERSION=5.8 docker compose build
# when ready:
docker compose down # stop the old version
UE_VERSION=5.8 docker compose up -dBoth images can sit on disk at once (they share base layers, so the incremental cost is just the size of one packaged build, typically a few hundred MB) — only one container can actually run at a time, since both bind the same host port.
- Yellow warning triangle next to the server name in the Multi-User
Browser — this is expected. The server runs with
-ConcertIgnore, which skips Concert's build/version-stamp check (necessary because a cross-compiled Linux server and your Windows Editor build get different stamps even from identical source). It's cosmetic, not an error. - Server doesn't show up in the Multi-User Browser — almost always
means the connecting machine isn't actually on the VPN. Check your
VPN client shows "Connected", and that the project's
Config/DefaultEngine.inihas theStaticEndpointsentry above. - "Read-only file system" warnings in the logs — a directory the
server needs to write to isn't covered by a volume mount. The
compose file mounts
Saved/andIntermediate/; if you see this for some other path, it needs its own mount too. - Session data grows and is never cleaned up — this is expected
behavior, not a bug. Concert has no automatic retention/expiry —
everyone's edits persist under
Intermediate/MultiUser/<sessionID>/until someone deletes them (or persists changes to source control and clears the session manually). Treat this as light periodic maintenance.
The tooling in this repo (Dockerfile, scripts, configuration) is MIT licensed — see LICENSE.
This repo does not include, and cannot include, Unreal Engine
itself. Epic's engine source and binaries are licensed separately
and can't be redistributed outside Epic's own GitHub org — that's why
build.ps1 builds the server binary from your own licensed source
checkout rather than shipping a prebuilt one. You need your own Epic
Games account with engine source access to use this repo at all.