Your server runs
You already start the game and it accepts players. Guard doesn't manage the game process for you.
You already run an AA, Spearhead, or Breakthrough server on Linux. This guide adds
Guard on top of it: drop in the HUD PK3, paste a few cvars into server.cfg,
add two lines to your precache script, and run one installer that sets the Python
sidecar up as a systemd service — it survives reboots, restarts
itself, and logs to journald. Nothing about your existing server changes — not
the launcher, not the binaries, not your firewall.
You already start the game and it accepts players. Guard doesn't manage the game process for you.
The sidecar is Python and the optional firewall layer uses nft. Ubuntu 24.04 is the tested target.
rconPassword "..." is set in server.cfg. The sidecar uses it to kick; the password never leaves your box.
You extract the bundle anywhere (your home directory is fine) and run the installer from there. The installer copies the sidecar to a fixed system location and keeps your secrets in a root-only config file — never inside the game folder.
/home/your-user/
moharena-guard-server-owner/ # extracted bundle — run the installer from here
install-systemd.sh # the one script you run (as root)
server-owner-install.sh # apt packages helper (the installer calls apt too)
sidecar.py # copied to /opt by the installer
moharena-guard@.service # systemd unit template
your-existing-game-server/
main/ | mainta/ | maintt/
qconsole.log # game log stays with the game
zzzzzz_stats*.pk3 # optional private stats
zz_moharena_ac_hud_server*.pk3 # copied here
/opt/moharena-guard/sidecar.py # installed sidecar (self-updates live here)
/etc/moharena/guard-<game>.env # your settings — root-only, mode 600
/etc/systemd/system/moharena-guard@.service
aa-original, aa-openmohaa, sh-original, sh-openmohaa, bt-original, bt-openmohaa.203.0.113.10:12205. The token mtc generates is bound to this exact address.7. You'll paste it as MOHARENA_GUARD_SERVER_ID.mohg_…. Shown on the website — treat it like a password.<ip>:<port> you sent — mtc echoes it back so you paste the exact string.
The bundle contains the installer (install-systemd.sh), the systemd unit
template, sidecar.py, and the server PK3s. Download it straight onto the VPS
(as whatever user already runs your game):
Copy zz_moharena_ac_hud_server.pk3 into the same folder as your
existing paks — the main/ folder for AA, mainta/ for SH, or
maintt/ for BT (use the OpenMoHAA equivalents if you run that). Add
zzzzzz_stats.pk3 only if you want private server stats.
Open your precache pk3 (the one that holds maps/dm/precache.scr or your equivalent)
and append the lines below. The HUD script is the important Guard piece; the stats script is
optional and only needed for private server stats.
server.cfg
These default to a sensible "AC visible, no kicks" posture — you can change the policy later
from the Guard website without restarting anything. Paste them at the end of your existing
server.cfg.
Logging cvars Guard depends on. The sidecar reads qconsole.log, so the
engine has to actually write the connect / chatter lines it parses. Add these too (the first three are
required; logfile just controls flushing):
logfile values: 0 = off, 1 = on but
buffered (the newest lines can be lost if the server crashes), 2 = on and
flushed after every line. Any value above 1 behaves the same as 2,
so 2 and 3 are equivalent. Use 2 (or 3) so Guard
sees events in real time — 1 works but can lag behind.
qconsole.log must be where Guard expects it. Point
MOHARENA_QCONSOLE (next step) at the file the engine actually writes — usually your game's
main folder (main/ for AA, mainta/ for SH, maintt/
for BT). If your engine writes it somewhere else (e.g. the server root), symlink it into place so both
paths point at the same file:
ln -s /home/mohaa/server2/qconsole.log /home/mohaa/server2/main/qconsole.log
Pass the three values from mtc plus your existing RCON password, qconsole.log path, and game
as flags. It installs and starts moharena-guard@<game> — which survives
reboots, restarts itself after crashes and self-updates, and logs to journald.
--server-cfg-path points at your server.cfg. Set it now —
it's what powers the Config tab on the website (view and edit your live config from the dashboard).
Without it that tab can't fetch anything.
Recovery copies. The first time you save an edit from the Config tab, Guard keeps two backups
next to your server.cfg so you can recover over SSH if an edit ever breaks the server:
server.cfg.moharena-bak (the pristine original) and server.cfg.moharena-prev
(the version from just before your last edit).
--runtime is original for the retail game (the default) or
openmohaa if you run the OpenMoHAA engine. It must match how you registered the server with mtc.
Prefer not to type flags? Open install-systemd.sh, fill in the
CONFIG block at the top (same field names), and run sudo ./install-systemd.sh
with no flags. Migrating from the old tmux wrapper? See MIGRATION.md in the bundle.
Several servers on one box? Run the installer once per server. Different games are
automatic (each --game is its own service). For two servers of the same game
(e.g. two aa servers), give each a distinct --instance name — otherwise the
installer stops to stop you overwriting the first:
sudo ./install-systemd.sh --game aa --instance aa1 --server-id 7 …
sudo ./install-systemd.sh --game aa --instance aa2 --server-id 8 …
About the firewall layer. Guard blocks players two ways: Layer 1 RCON-kicks
(always on, no setup), and Layer 2 kernel-level packet drops via nftables.
With the systemd service this just works: the unit grants the sidecar the one kernel capability
nft needs (CAP_NET_ADMIN), so there is no sudoers entry and no running as
root. Set MOHARENA_GUARD_FIREWALL_BLOCKS=0 in the env file if you want RCON kicks only.
All your settings live in /etc/moharena/guard-<game>.env (root-only,
mode 600) — the same options the old wrapper had, now with comments. Edit any value, then apply
it with sudo systemctl restart moharena-guard@<game>:
Open your server's page on Guard. The Overview tab shows the last heartbeat (should refresh every few seconds). Connect a player and the Players tab fills in within 10 seconds. The in-game HUD reads AC Active — N/M.
Here are some useful commands that you might want to use.
systemctl status moharena-guard@shIs the sidecar running? Swap sh for aa/bt on those games.journalctl -u moharena-guard@sh -fFollow the live sidecar log.journalctl -u moharena-guard@sh | grep FIREWALL_LAYERDid the Layer 2 nftables probe succeed at startup?sudo nft list table inet moharena_guardShow the live kernel set of dropped IPs/CIDRs.sudo systemctl restart moharena-guard@shApply changes after editing /etc/moharena/guard-sh.env or rotating the token.sudo systemctl stop moharena-guard@shStop the sidecar. start brings it back; it also starts on boot.Two PK3s in your pak folder, ~10 lines in server.cfg, 4 lines in your precache script, and one installer run (sudo ./install-systemd.sh) that sets the sidecar up as a systemd service with your RCON password.
No. Setup is entirely self-serve. mtc only registers a Guard record on the website and DMs you three values. Nothing on your machine talks to mtc directly — the sidecar talks only to guard.moharena.com.
No. The password stays in /etc/moharena/guard-<game>.env on your VPS — readable by root only. The sidecar uses it locally to issue kicks. The website never sees it.
Only if you tell it to. The defaults in this guide are "AC visible, no kicks". You can change the policy on the website's Policy tab. Changes apply within ~30 seconds without restarting anything.
Blocks are exact-match, per-player or per-IP, manual entries (one button on the Players tab). Rules are patterns — IP exact, IP range, CIDR, wildcard prefix, country code, ASN, or "every VPN/proxy IP". A matching rule with action Permanent block auto-creates a block.
That means the sidecar is up but the website's live policy isn't asking for AC enforcement. Open the server's Policy tab and tick Show "AC Active" badge in-game. The HUD flips within ~30 seconds.
Your client has the auth_token cvar saved from a previous launcher session. The sidecar now only marks players as verified when the launcher is actively heartbeating — if you see this, make sure you are on the latest sidecar (mtc can push it remotely, or update by hand) and run sudo systemctl restart moharena-guard@<game>.
No. The installer runs as root once (to write the system files), but the service itself runs as your game-server user. The nftables firewall layer works anyway: the systemd unit grants the one kernel capability nft needs (CAP_NET_ADMIN) — no sudoers entry, no root process.
MOHARENA_GUARD_FIREWALL_LAYER mode=rcon-only?The sidecar tried nft at startup and couldn't call it. RCON-kick still works fine; you just don't get the kernel-level packet drop. With the systemd service this shouldn't happen (the unit grants the capability and the right PATH) — check nft --version is installed, or set MOHARENA_GUARD_FIREWALL_BLOCKS=0 in the env file to silence the message.
Usually you don't have to do anything — mtc pushes releases from the website, the sidecar swaps its own file, and systemd restarts it on the new code within seconds. To update by hand: replace /opt/moharena-guard/sidecar.py with the latest from the bundle and run sudo systemctl restart moharena-guard@<game>. To refuse remote pushes entirely, set MOHARENA_SIDECAR_AUTO_UPDATE=0 in the env file.
Run sudo systemctl disable --now moharena-guard@<game>, delete the Guard PK3s from your pak folder, and comment out the Guard exec lines you added to precache. Your server is back to its pre-Guard state. The Guard cvars in server.cfg are harmless when the sidecar isn't running.
Send the output of journalctl -u moharena-guard@<game> -n 50 --no-pager, the Guard server ID, and whatever the in-game HUD reads. Don't paste your mohg_... token or the RCON password.