Installing a Rust server on Linux

Installing RustDedicated with SteamCMD under a dedicated user, the start script and its launch arguments, world size and seed, and systemd.

Game servers6 min read

On this page
  1. Install it
  2. The launch arguments that matter
  3. World size and seed decide the map
  4. After the first boot
  5. Run it under systemd
  6. Open the ports
  7. Related

This builds a Rust server from nothing on a 64-bit Linux VPS: SteamCMD, a dedicated non-root user, a start script you can read, and a systemd unit that survives a reboot. Commands are for Debian 12 and Ubuntu 22.04 or newer, with the RHEL family equivalents given where they differ.

You need root or sudo on the server, and a way in. See Connecting to your server if you do not have an SSH session yet.

Install it

  1. Create a user for the server

    Rust does not need root and should not have it. A game server that runs as root turns any plugin flaw into a whole-machine problem.

    Shell
    sudo useradd -m -s /bin/bash rust

    Everything below happens inside that account's home directory, /home/rust.

  2. Install the SteamCMD prerequisites

    SteamCMD is a 32-bit binary, so a 64-bit host needs the 32-bit C and C++ runtimes even though the Rust server itself is 64-bit.

    On Debian and Ubuntu:

    Shell
    sudo dpkg --add-architecture i386
    sudo apt update
    sudo apt install -y curl tar ca-certificates lib32gcc-s1 lib32stdc++6

    On RHEL, AlmaLinux, and Rocky:

    Shell
    sudo dnf install -y curl tar glibc.i686 libstdc++.i686
  3. Install SteamCMD

    Switch to the rust user and unpack it into its own directory:

    Shell
    sudo -u rust -i
    mkdir -p ~/steamcmd && cd ~/steamcmd
    curl -sSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz | tar zx

    Run it once on its own so it can self-update before you ask it for anything:

    Shell
    ~/steamcmd/steamcmd.sh +quit
  4. Download the Rust server

    The Rust dedicated server is Steam app 258550 and installs anonymously, with no account and no licence:

    Shell
    ~/steamcmd/steamcmd.sh \
      +force_install_dir /home/rust/server \
      +login anonymous \
      +app_update 258550 validate \
      +quit

    It is a large download. The same command is how you update later, so keep it somewhere you can copy it from.

  5. Link steamclient.so

    The server looks for the Steam client library in a fixed location and fails to start cleanly if it is missing:

    Shell
    mkdir -p ~/.steam/sdk64
    ln -s ~/steamcmd/linux64/steamclient.so ~/.steam/sdk64/steamclient.so
  6. Write the start script

    Create /home/rust/server/start.sh:

    Shell
    #!/usr/bin/env bash
    set -euo pipefail
    cd /home/rust/server
     
    exec ./RustDedicated -batchmode -nographics \
      +server.identity "myserver" \
      +server.hostname "My Rust server" \
      +server.description "Weekly wipes. Monthly BP wipe." \
      +server.port 28015 \
      +server.queryport 28017 \
      +rcon.port 28016 \
      +rcon.password "a-long-random-password" \
      +rcon.web 1 \
      +server.maxplayers 100 \
      +server.worldsize 3000 \
      +server.seed 1500000 \
      +server.saveinterval 300 \
      +server.level "Procedural Map"

    Make it executable, and keep it readable only by the server's own user, because the RCON password is sitting in it:

    Shell
    chmod 700 /home/rust/server/start.sh
  7. Start it by hand once

    Before wiring up systemd, run it in your own terminal so you can watch it:

    Shell
    /home/rust/server/start.sh

    The first boot generates the map, which takes several minutes and pins a core while it runs. Nothing answers on any port until it finishes, so a server that looks dead in the first few minutes is usually working normally. Wait for the console to report that startup is complete, then press Ctrl+C once and let it save and exit.

The launch arguments that matter

Rust takes its configuration as console variables on the command line, each prefixed with +. These are the ones you will actually set:

ArgumentWhat it does
-batchmode -nographicsRuns headless, with no window and no renderer. Both are required on a server.
+server.identityNames this instance. Everything the server saves lives in a directory of this name, so changing it gives you an empty world and a blank config.
+server.portUDP port for game traffic.
+server.queryportUDP port that answers Steam server queries. Set it explicitly so you know which port to open.
+rcon.portTCP port for the remote console.
+rcon.passwordRCON is disabled until this is set. Make it long and do not reuse it.
+rcon.web 1Serves RCON over websockets, which is what most current admin tools and Discord bots speak.
+server.maxplayersSlot count. Raising it raises memory and CPU.
+server.worldsizeMap size. See below.
+server.seedMap seed. See below.
+server.saveintervalSeconds between saves. You lose up to this much progress in a crash.
+server.hostname, +server.description, +server.url, +server.headerimageWhat players see in the server browser.
+server.levelThe map generator. "Procedural Map" is the standard one.

World size and seed decide the map

The map is not stored as a design, it is generated. The seed and the world size together are the map: the same pair always produces the same terrain, monuments, and roads, and any other pair produces a different world.

That is why the save file is named after them. Look in your server's identity directory and you will see something in the shape of proceduralmap.<worldsize>.<seed>.<version>.sav.

Same size, same seedLaunch arguments+server.worldsize 3000+server.seed 1500000The same mapSame terrain, monuments and roads.The save carries onproceduralmap.3000.1500000.<version>.savNamed after the pair. Every base is stillthere.One of them changedLaunch arguments+server.worldsize 4000+server.seed 1500000A different mapAny other pair produces a different world.The old save no longer appliesEvery base, box and deployable is gone.This is a map wipe.
The world size and the seed are the map. Keep both and the save named after them carries on; change either one and the server builds a different world that the old save does not fit, which is a map wipe. Illustration; values from the start script on this page.

Size is a direct trade. A larger world takes longer to generate on first boot, holds more monuments and more players comfortably, costs more memory, and takes players longer to cross. Most servers sit in the 3000 to 4500 band, and smaller maps are a legitimate way to make a low-population server feel busy. Pick a size you can afford at the end of a wipe cycle, not one that only fits on wipe day.

If you have no seed preference, any integer works. Once you have one you like, record the seed and size together, because that pair is the only way back to that map.

After the first boot

Starting the server once creates its configuration, under /home/rust/server/server/myserver/cfg/ if your identity is myserver. The doubled server in that path is correct: the first is your install directory, the second is where Rust keeps per-identity data.

Three files there are worth knowing:

  • server.cfg, console variables run at startup.
  • users.cfg, which holds ownerid and moderatorid lines. This is how you give yourself admin, using your own 17-digit Steam ID.
  • bans.cfg.

Edit them while the server is stopped. A running server rewrites users.cfg and bans.cfg itself, so changes made underneath it get overwritten.

Run it under systemd

The remaining commands are run as yourself, not as rust. Type exit to leave the server account first.

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

Config
[Unit]
Description=Rust dedicated server
After=network-online.target
Wants=network-online.target
 
[Service]
Type=simple
User=rust
Group=rust
WorkingDirectory=/home/rust/server
ExecStart=/home/rust/server/start.sh
Restart=on-failure
RestartSec=15
KillSignal=SIGINT
TimeoutStopSec=180
 
[Install]
WantedBy=multi-user.target

Then:

Shell
sudo systemctl daemon-reload
sudo systemctl enable --now rust
sudo systemctl status rust
journalctl -u rust -f

Two lines in that unit are doing real work. KillSignal=SIGINT is what lets Rust save and shut down properly, because it handles an interrupt the way the console does; stopping it with the default signal loses up to one save interval. TimeoutStopSec=180 gives that save time to finish before systemd kills the process outright. The start script deliberately does not pass -logfile, so output goes to the journal where journalctl can follow it.

The manual update, which is also what you run on the first Thursday of each month:

Shell
sudo systemctl stop rust
sudo -u rust /home/rust/steamcmd/steamcmd.sh \
  +force_install_dir /home/rust/server \
  +login anonymous \
  +app_update 258550 validate \
  +quit

Update Oxide or Carbon at this point, while the server is still stopped, then start it:

Shell
sudo systemctl start rust
In orderStop the serversudo systemctl stop rustUpdate RustSteamCMD, app 258550.Update Oxide or CarbonWhile the server is still stopped.Start itsudo systemctl start rustUp, plugins loadedFramework not yet updatedRust is updatedBy hand, or by an update in ExecStartPre aftera crash.The server startsOxide or Carbon is still built for the oldRust.DownPlugins fail to load, or the server doesnot boot at all.
Rust, then the framework, then start. Started before Oxide or Carbon has caught up, the server comes up against a framework built for the old Rust and stays down, which is what an update in ExecStartPre does on its own after a crash. Illustration.

Open the ports

The server is running, but nothing reaches it until the ports are open at the network edge. For the start script above that is UDP 28015, UDP 28017, and TCP 28016. Add a rule per port and protocol under Port rules, or apply the Rust entry in Game presets and add the query port yourself.

If the server runs but players cannot reach it, work through When players cannot connect.

Need a server, or a hand with one?

Pick a plan to deploy a server, or open a ticket in the panel. Support runs 24/7/365.