Linux dedicated server
The dedicated server ships as native Linux binaries inside every build archive, under server-linux/:
M2OServer— the server executablelibnode.so.141— the bundled Node.js runtime that executes server resourcescrashpad_handler— the crash reporter (optional to run, keep it next to the server)
Requirements
Section titled “Requirements”- x86-64 Linux with glibc 2.38 or newer — Ubuntu 24.04+, Debian 13 (trixie)+.
On older distributions (Ubuntu 22.04, Debian 12) the binary does not start: the
loader reports missing
GLIBC_2.38/GLIBCXX_3.4.32symbols. There is no workaround short of upgrading the distribution or running a newer one in a container. - The usual runtime libraries. On a minimal Ubuntu/Debian install:
sudo apt-get updatesudo apt-get install -y libatomic1 libstdc++6 libgcc-s1 libcurl4 openssl ca-certificates unzipInstall
Section titled “Install”Unpack server-linux/ from the build archive into a directory of its own and make the binaries executable — a zip transfer does not always preserve the executable bit:
mkdir -p ~/m2o-server && cd ~/m2o-serverunzip -j /path/to/M2O-<version>.zip '*server-linux/*' -d .chmod +x M2OServer crashpad_handlerPut your resources next to the binaries, one directory per resource:
~/m2o-server/├── M2OServer├── libnode.so.141├── crashpad_handler└── resources/ └── my-gamemode/ ├── package.json ├── server/main.js └── client/main.jsEach resource declares its entry points in package.json; only the client/ part of a resource is streamed to players:
{ "name": "my-gamemode", "version": "0.1.0", "mafiahub": { "server": "server/main.js", "client": "client/main.js", "priority": 20 }}cd ~/m2o-serverexport LD_LIBRARY_PATH=.export TRACY_NO_INVARIANT_CHECK=1./M2OServerLD_LIBRARY_PATH=.lets the loader findlibnode.so.141next to the executable.TRACY_NO_INVARIANT_CHECK=1matters on VPS and older CPUs: without an invariant TSC the embedded profiler aborts the server on startup with exit code 1 and no useful message. Setting the variable is harmless everywhere, so just always set it.
Open the firewall
Section titled “Open the firewall”The game talks over UDP on the server port (default 27015). Open it, e.g. with ufw:
sudo ufw allow 27015/udpIf players see the server but time out while connecting (the log shows an incoming
connection request, then Connection lost after ~20 s), the usual culprits are a
closed/unforwarded UDP port or a path with a reduced MTU (VPN or tunnel) between
the player and the server.
Keep it running
Section titled “Keep it running”A minimal systemd unit:
[Unit]Description=M2O dedicated serverAfter=network.target
[Service]WorkingDirectory=/home/m2o/m2o-serverEnvironment=LD_LIBRARY_PATH=.Environment=TRACY_NO_INVARIANT_CHECK=1ExecStart=/home/m2o/m2o-server/M2OServerRestart=on-failureUser=m2o
[Install]WantedBy=multi-user.targetsudo systemctl daemon-reloadsudo systemctl enable --now m2oserverjournalctl -u m2oserver -fThe same layout works fine inside Docker — use an Ubuntu 24.04 (or newer) base image so the glibc requirement is met, install the packages from step 1, and set the same two environment variables.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause |
|---|---|
version 'GLIBC_2.38' not found / GLIBCXX_3.4.32 on start |
Distribution too old — use Ubuntu 24.04+ / Debian 13+ (bare or as container base) |
| Exits with code 1 immediately on a VPS | No invariant TSC — set TRACY_NO_INVARIANT_CHECK=1 |
error while loading shared libraries: libnode.so.141 |
LD_LIBRARY_PATH does not include the server directory |
Players time out after Incoming connection request |
UDP port closed/unforwarded, or reduced-MTU path (VPN/tunnel) on the player side |
| A resource silently does nothing | Syntax error in its JS — check the server log at startup; validate with node --check before shipping |
