This page takes a Linux machine from nothing to a live RivalityFX server players
can connect to. It targets a plain Linux VPS (OVH, Hetzner, a cloud VM…) — the
game server only. The public website + leaderboard are a separate, optional layer
(§9).
The repo ships a small Makefile that automates the three setup chores — fetch the
runtime, create the secrets file, run — so most of this page is four make commands and
a systemd unit.
your players ──(UDP/TCP :30120)──▶ Linux VM ───────────────┐ FXServer (run.sh) │ optional, outbound HTTPS └─ RivalityFX resources └──▶ stats API + website (§9)
Port note. This repo uses FiveM's default 30120 (TCP + UDP). If you change it in
your server.cfgendpoint_add_* lines, use that port wherever you see 30120 below — it
must match your firewall rule.
A Linux VM. The FXServer Linux artifact is a self-contained build (it ships its own
alpine/ runtime), so the host distro barely matters — Debian 12 / Ubuntu 22.04+ are
the well-trodden path. Size for your peak: 2 vCPU / 4–8 GB RAM comfortably runs a
handful of concurrent scenes (rfx_max_scenes, default 8).
A cfx.re server license key — free, from portal.cfx.re.
Required to go public. Lock it to your server's static IP in the portal.
The RivalityFX resources on the box (this repository — resources/, server.cfg,
boot.cfg, Makefile, server_secrets.cfg.example).
Shell tools the Makefile uses to fetch the runtime:
Ship the repo to a working directory — /opt/rivalityfx is a good choice. You only need
the server-side tree; leave the build runtime and the website behind:
# from your workstation — exclude the things the game server never readsrsync -a --exclude alpine/ --exclude cache/ --exclude fx.tar.xz \ --exclude www/ --exclude services/ --exclude node_modules/ --exclude .git/ \ ./ user@your-vm:/opt/rivalityfx/
The pieces that matter at the root:
File / dir
Role
resources/
Every resource ([platform]/, [modes]/, [packs]/, [systems]/).
server.cfg
The one file FXServer reads at boot. The full reference is Server configuration.
This resolves the latest recommended Linux FXServer build, downloads it, and extracts
alpine/ + run.sh next to your config. Re-running is a no-op once present; to force a
fresh build later, make clean then make runtime. Pick a different channel with
FX_CHANNEL (recommended · latest · optional · critical):
make runtime FX_CHANNEL=latest
Pin the game build, not just the runtime.server.cfg sets sv_enforceGameBuild
(this repo: 3258) so every client runs the GTA assets/natives the arenas were built
against. Bumping the FXServer runtime and bumping the game build are independent — change
the game build only deliberately, and re-test maps after.
make secrets # copies server_secrets.cfg.example → server_secrets.cfg (if missing)
Then edit server_secrets.cfg and set your license key. This file is gitignored —
secrets never live in server.cfg:
sv_licenseKey "cfxk_…" # from portal.cfx.re — lock it to this VM's IPset steam_webApiKey "" # optional; "" or "none" is fine#set rcon_password "CHANGE_ME" # only if you use RCON#set rfx_api_token "CHANGE_ME" # only when rfx_store=api (the website layer — §9)
You can stop here for a public server: the license key is the only required secret. The
API token is for the optional stats stack (§9).
The repo ships a complete, annotated server.cfg — the full line-by-line reference is
Server configuration. The smallest file that boots a playable 1v1
server (KVP store, no website) is:
ensure order is not cosmetic.sdk registers the mode/pack/arena registries that
later resources consume; the engine reads its store/identity convars at first boot. The
Server configuration page covers the
full ordering and the few convars that must sit aboveensure engine.
A healthy boot prints the SDK registering each resource:
[sdk] mode registered: duel (1v1 Duel) from mode_duel[sdk] battleground pool: N battlegrounds registered from battleground_pack_base
Connect from the FiveM client — open the F8 console and connect <your-ip>:30120, or use
the in-game direct connect. You spawn in the lobby; press M for the menu.
Nothing is matchmade yet. A fresh server boots with zero open arenas — see the next
step. As a quick solo smoke test, set rfx_lab true and use /rfx lab helpers
(then turn it back off for production).
Public arenas live in the engine's memory and are wiped on every restart, so they're
(re)opened from boot.cfg — exec'd last, after all resources are up. The shipped file
is fully commented; uncomment what you want:
# boot.cfg — ONE Quick Play queue + ONE ranked queue per mode.# A queue is a template: one menu card, instances materialise on demand.rfx queue open duel 5 s0 # Quick Play duel, RANDOM weapon, best-of 5rfx ranked open duel 5 s0 # the ranked ladder for the same moderfx queue open battle_royale s0 # royale — fills, then launches on a countdown
The complete command — scoring, best-of, map, stamina, private codes — and the operating
model are in Operations & ranked. For
copy-paste server shapes (pure ladder, casual party, royale night…), see
Configuration recipes.
Why script wraps run.sh — the #1 systemd gotcha. Launched directly (no txAdmin)
under systemd, FXServer reads its console stdin, hits EOF on systemd's null stdin, and
reads that as "Ctrl-C pressed in server console" — so it self-quits right after a clean
boot, and Restart=always turns it into a crash loop. The pseudo-terminal keeps stdin open
so it stays up; -e makes script exit with the server's own code, so Restart= still
catches real crashes. (tmux/screen work too — script is the smallest systemd-native
option. Not needed if you front the server with txAdmin, which owns the process itself.)
LimitNOFILE=65536 raises the open-file limit FXServer needs under load — don't omit
it. The dedicated fivem user keeps the server off root.
Day-to-day. Under systemd the server runs headless — there's no interactive console,
so apply changes with a full sudo systemctl restart rivalityfx (which replays boot.cfg,
so your arenas come back). The per-resource restart <name> / refresh commands (no full
restart) are console commands — available when you run attached via make run, or in-game
to an admin via /rfx reload when rfx_dev_commands is on (keep it off in production).
Boot loop? Two symptoms people hit here:
Quitting: Ctrl-C pressed in server console right after a clean boot → the stdin/pty
issue above. Check your ExecStart uses the script wrapper.
HTTP 429: Too Many Requests while "Authenticating server license key" → cfx.re
rate-limited you because a crash loop re-checked the license every few seconds. It clears
on its own once the server stops restarting — fix the loop, wait a minute, retry.
Open the game port — TCP and UDP — on both the cloud firewall and the host:
sudo ufw allow 30120/tcpsudo ufw allow 30120/udp
Match this to your endpoint_add_* port. On a cloud provider, add the same rule to the
instance's firewall/security group.
Declare your connect address. The server can't detect its own public address, so set
rfx_connect in server.cfg (IP:port, a domain, or a cfx join code) — it's shown on the
website join card and the in-game UI. See
Server configuration §5.
Clean connect address. On the default port 30120, players connect to a bare
play.example.com (no :port) as soon as an A record points it at your static IP.
An _cfx._udpSRV record is only needed to hide a non-default port:
Everything above runs a complete, self-contained server on the kvp store (no
database). The optional layer adds a cross-player leaderboard + public website and is
what makes selling VIP / packs possible:
Switch rfx_store to api and point rfx_api_url at a deployed stats API
(rfx_api_token goes in server_secrets.cfg). Without api, KVP can't build a
leaderboard. See Server configuration §5.
The stats stack (a FastAPI service + a Next.js site, designed for Cloud Run + Datastore)
is its own deployment. The Tebex purchase pipeline rides on rfx_store=api — read
Selling VIP & packs for what it unlocks and how to wire it.
[ ] make runtime # FXServer downloaded (alpine/ + run.sh)[ ] make secrets + sv_licenseKey # cfx.re key set, IP-locked[ ] server.cfg # endpoint, ensure block, your admin principal[ ] systemd unit enabled # auto-start + restart-on-crash + journal logs[ ] firewall: TCP+UDP open # same port as endpoint_add_*[ ] rfx_connect set # players know where to connect[ ] boot.cfg # the public arenas you want at boot
Before real traffic: run through Security & hardening — the
dedicated service user, secret permissions, player-IP privacy, SSH and firewall lockdown.