Rust Field guide

Set up a vanilla Rust server on Ubuntu: ports, RCON and world backups

Run vanilla Rust on Ubuntu with SteamCMD, a stable identity, separate game/query ports, private RCON and world backups.

Set up a vanilla Rust server on Ubuntu: ports, RCON and world backups

Install Rust PC's public server branch under its own Linux account, separate player traffic from administration, and preserve the world before updates. The commands target a fresh Ubuntu 24.04 x86_64 VPS with a public IP, sudo access and a Rust PC client. Console Edition, plugins and custom maps need different workflows.

Facepunch's setup page calls for 12 GB free RAM and 15 GB disk space; larger maps need more memory. Reserve OS headroom and extra disk space for updates and archives. A VPS with 16 GB total RAM is an initial allocation to evaluate, not a promise of player capacity. This example uses a 3000-size procedural map and a ten-player limit.

Players and the browser use different UDP ports. RCON remains on loopback; copy the identity only after Rust has saved and exited.

Prepare a dedicated account and SteamCMD#

Run the following on the VPS. The 32-bit runtime libraries support SteamCMD; the game itself is a separate process. Avoid running either as root.

bash
sudo apt update
sudo apt install -y ca-certificates curl tar lib32gcc-s1 lib32stdc++6 nano ufw openssl
sudo adduser --system --group --home /srv/rust rust
sudo install -d -o rust -g rust -m 750 /srv/rust /srv/rust/steamcmd /srv/rust/game
sudo -u rust -H /bin/bash -c 'cd /srv/rust/steamcmd && curl -fsSLo steamcmd.tar.gz https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz && tar -xzf steamcmd.tar.gz'
sudo -u rust -H /srv/rust/steamcmd/steamcmd.sh +force_install_dir /srv/rust/game +login anonymous +app_update 258550 -beta public validate +quit

The explicit HOME from -H belongs to rust, so SteamCMD does not create account state under your SSH user's home. Facepunch documents the archive URL, anonymous login and app 258550. The selected public branch avoids accidentally inheriting a staging branch on later updates. Confirm SteamCMD reports a completed installation before proceeding; do not interpret a shell returning as proof it downloaded everything.

Keep identity and map parameters stable#

friends is this server's identity. Create its configuration directory before the first start:

bash
sudo install -d -o rust -g rust -m 750 /srv/rust/game/server/friends/cfg
sudo -u rust nano /srv/rust/game/server/friends/cfg/server.cfg

Put these settings in the file:

text
server.hostname "Friends vanilla server"
server.maxplayers 10
server.level "Procedural Map"
server.seed 123456
server.worldsize 3000

Create a random RCON password in a root-only environment file. RCON startup parameters belong on the launch command; keep ordinary game settings in server.cfg:

bash
sudo install -o root -g root -m 600 /dev/null /srv/rust/rcon.env
sudo /bin/bash -c 'umask 077; printf "RUST_RCON_PASSWORD=%s\n" "$(openssl rand -hex 24)" > /srv/rust/rcon.env'
sudo chmod 600 /srv/rust/game/server/friends/cfg/server.cfg

Read the password privately with sudo nano /srv/rust/rcon.env when configuring your local client. Systemd reads this file and passes the secret to the game. The startup argument can be visible to other users on the same host: use a VPS restricted to trusted administrators and omit full process arguments from shared diagnostics. Facepunch documents RCON address binding; we bind to loopback and verify the actual listener below. Keep RCON inaccessible from the public Internet even if the installed build handles binding differently.

The identity selects the data directory; changing it can make an existing world appear missing. Keep the identity, map size and seed in your backup notes. The identity-file reference describes saves, maps, player databases and their WAL companions. Copying only one .sav is not a complete backup.

Start the game as a service#

Create sudo nano /etc/systemd/system/rust.service:

ini
[Unit]
Description=Rust vanilla dedicated server
Wants=network-online.target
After=network-online.target

[Service]
Type=exec
User=rust
Group=rust
Environment=HOME=/srv/rust
EnvironmentFile=/srv/rust/rcon.env
UMask=0077
WorkingDirectory=/srv/rust/game
ExecStart=/srv/rust/game/RustDedicated -batchmode -nographics +server.identity friends +server.port 28015 +server.queryport 28017 +rcon.ip 127.0.0.1 +rcon.port 28016 +rcon.web 1 +rcon.password ${RUST_RCON_PASSWORD} +app.port 1- -logfile /srv/rust/rust.log
Restart=on-failure
RestartSec=15
TimeoutStopSec=180
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

The unit uses systemd's environment-file and restart settings; UMask=0077 keeps newly created logs and data private. Unity documents the headless launch flags.

Game traffic uses UDP 28015; server-browser queries use UDP 28017. They must be distinct. RCON uses TCP 28016 on loopback. Setting explicit values avoids relying on the query-port default, which also depends on the RCON port. Rust+ is deliberately disabled with the documented parser-compatible +app.port 1-; its extra connectivity is outside this setup.

bash
sudo systemctl daemon-reload
sudo systemctl enable --now rust
sudo systemctl status rust --no-pager
sudo tail -n 80 /srv/rust/rust.log
sudo ss -lunp
sudo ss -ltnp 'sport = :28016'

Wait for Server startup complete in the game log. Confirm both UDP listeners belong to Rust, and RCON listens on 127.0.0.1:28016. If it listens publicly, correct the configuration before administration; leave external TCP 28016 blocked. The log file is /srv/rust/rust.log; journalctl alone may not contain the game's detailed output. Monitor its disk use and archive it during maintenance rather than allowing it to grow indefinitely.

systemctl stop is not our proof of a clean world save. Maintenance below sends quit through RCON and checks that Rust finishes saving and exits before copying files.

Admit public game traffic and test a real join#

Before enabling UFW, allow your actual SSH port and preserve rules for other services. Here SSH uses 22:

bash
sudo ufw allow 22/tcp
sudo ufw allow 28015/udp
sudo ufw allow 28017/udp
sudo ufw enable
sudo ufw status

Keep the current session open and verify a second SSH connection. Also allow UDP 28015 and 28017 in the provider firewall. Do not add a public TCP 28016 rule. These host rules follow Ubuntu's firewall guide.

In the Rust PC client, press F1 and substitute the VPS address:

text
connect VPS_IP:28015

This tests direct game access. Browser discovery is a separate check using the query port and browser filters. If direct joining works but the listing is absent, investigate query reachability and filters before reinstalling the game.

The example is a public vanilla server: a hostname containing “friends” does not restrict admission. For a closed group, implement the separate Facepunch hidden/whitelisted-server workflow and test admission explicitly.

Reach local RCON through SSH#

On your computer, install a trusted WebRCON-compatible local client; Rustadmin's free desktop application is one option. Its hosted online product cannot reach this private tunnel. Open a tunnel from your computer, replacing the SSH account/address; add -p YOUR_SSH_PORT if necessary:

bash
ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:28016:127.0.0.1:28016 SSH_USER@VPS_IP

Leave that terminal open. Configure the local client for WebRCON, host 127.0.0.1, RCON port 28016 and the password stored in rcon.env. If the client's game/query polling fails through this TCP tunnel, that does not itself prove RCON failed; the UDP endpoints are separate. Test the command console with find save. You should receive the installed build's command information rather than a connection error.

Make a backup after a confirmed graceful quit#

Warn players first. In the RCON command console, send each command separately, waiting for saving to finish:

text
server.save
quit

Facepunch's server save/quit implementation record documents a final save on quit. Confirm actual behavior for your installed build: in the VPS SSH session, inspect the end of the log and service/process state:

bash
sudo tail -n 80 /srv/rust/rust.log
sudo systemctl show rust -p ActiveState -p Result -p MainPID
sudo ss -lunp

Continue only when the save has finished, Rust has exited, ActiveState=inactive, Result=success, MainPID=0, and the game sockets are gone. MainPID=0 alone can also occur while a failed service is waiting to restart. A disconnected RCON client does not prove completion. If saving fails, the process restarts or a forced kill occurs, resolve it before calling the following archive a consistent backup.

Check free space, archive the installation and account state, validate the archive and stage a restricted copy for retrieval:

bash
(
set -e
sudo df -h /srv /var/backups
sudo install -d -m 700 /var/backups/rust
RUST_BACKUP="/var/backups/rust/backup-$(date +%F-%H%M%S).tar.gz"
sudo tar -C /srv -czf "$RUST_BACKUP" rust
sudo tar -tzf "$RUST_BACKUP" > /dev/null
sudo install -m 600 -o "$(id -un)" "$RUST_BACKUP" "$HOME/rust-backup.tar.gz"
sudo systemctl start rust
)

If a command fails, stop and investigate. Restarting can end the maintenance window, but a failed archive is not a backup. Retrieve the staged file from your computer:

bash
scp SSH_USER@VPS_IP:rust-backup.tar.gz ./rust-backup.tar.gz

For custom SSH ports add -P YOUR_SSH_PORT to scp. The archive contains the RCON secret and player data: restrict access and retain several dated copies off the VPS. Listing an archive verifies readability, not successful game restoration. Test extraction and the world on an isolated compatible server before relying on it.

Update without treating every restart as a wipe#

Make and retrieve a backup, quit cleanly as above, then rerun the same SteamCMD command:

bash
sudo -u rust -H /srv/rust/steamcmd/steamcmd.sh +force_install_dir /srv/rust/game +login anonymous +app_update 258550 -beta public validate +quit
sudo systemctl start rust
sudo tail -n 80 /srv/rust/rust.log

Preserve the old build record with its world and verify a real join after updating. Do not put SteamCMD validation into every startup: it makes restart behavior depend on the current release and can replace modded files if you later install extensions.

A forced map wipe can advance the save protocol and create a new world; it does not mean every update clears blueprints. Facepunch explains the distinction and warns against renaming old saves to bypass a forced wipe. Changing seed, size or identity is also consequential. If the world appears reset unexpectedly, preserve all existing files and compare those parameters and protocol before deleting anything.