How to Automate Rust Server Wipes on Linux

Automate Rust server wipes on Linux with one bash script and a systemd timer: map or full wipes, a new seed, RCON warnings and forced wipe day updates.

Last updated Verified on Identity folder of Rust build 25581777, systemd 255 calendar checks, 2026-09-28

On this page

Most Rust servers wipe on a fixed rhythm: weekly or every two weeks, plus the forced wipe Facepunch ships with the monthly update. Doing that by hand at 7 in the evening gets old fast. This guide builds one small bash script that warns players, stops the server, deletes the right files, picks a new seed and starts it again, then schedules it with a systemd timer. The same script handles forced wipe day: it waits for the new build, updates with SteamCMD and reinstalls Oxide.

It assumes the layout from How to install a Rust server on Ubuntu: the server in /opt/rust-servers/instance-1, identity my_server, a systemd unit called rust-server. Change the variables at the top of the script if yours differ. If you want to know what a wipe is before you automate one, start with How to wipe a Rust server.

What each wipe deletes

Everything a wipe touches lives in the identity folder, server/<identity>/ in the install directory. This is the real content of the one on our test host, trimmed:

cfg/
player.blueprints.18.db
player.blueprints.18.db-wal
player.deaths.18.db
player.identities.18.db
player.states.289.db
player.tokens.db
clans.289.db
relationship.289.db
sv.files.289.db
proceduralmap.4000.1420068679.289.map
proceduralmap.4000.1420068679.289.sav
proceduralmap.4000.1420068679.289.sav.1
proceduralmap.4000.1420068679.289.sav.2
proceduralmap.4000.1420068679.289.navmesh
proceduralmap.4000.1420068679.289_occlusion_3.dat

The map files are named after world size, seed and save version: 4000 is the world size, 1420068679 the seed.

WipeDeletesPlayers keep
Map*.sav, the backups *.sav.1, *.sav.2 and *.sav.new, *.map, *.navmesh, *.navmesh.new, *_occlusion_*.dat, sv.files.*.dbBlueprints, identities, clans, contacts
FullMap files plus player.blueprints.*.db and player.states.*.dbIdentities, tokens, clans, contacts

SQLite databases go together with their -wal, -shm and -journal side files. That is exactly what the Panelra agent deletes for a map-only and a full wipe.

Why each of these goes:

  • Save backups. Rust keeps rolling copies .sav.1, .sav.2 (how many is set by server.savebackupcount, at least 2) and shifts them on every save. The server only ever loads the plain .sav and never falls back to a backup, but if you wipe and keep the seed, last wipe's .sav.1 sits under the new world's name until the new map has saved twice. Delete it so nobody restores it by mistake.
  • Navmesh and occlusion caches. The navmesh is only loaded together with a save, so a fresh world rebuilds it anyway. Old ones just take space: on our host each navmesh is 140 to 175 MB.
  • sv.files. It holds the images painted on signs and photo frames, keyed by the id of the sign they sit on. Without the save those ids point at nothing, and the server never cleans the file up.
  • player.states (full wipe). Per-player progress such as missions and explored map. The server resets it by itself for a different seed or save, so keeping it on a map wipe is harmless.

Never delete these files while the server runs. The next autosave, or the save Rust does when systemd stops it, writes the old world straight back and the wipe silently never happens. The script stops the server and waits until systemd reports it inactive before it touches anything.

Warning players over RCON

Rust's RCON is WebSocket based when you start the server with +rcon.web 1, so a plain Source RCON tool will not talk to it. rcon-cli by gorcon supports it with -t web. Download rcon-0.10.3-amd64_linux.tar.gz from its releases page, extract it and copy the rcon binary to /usr/local/bin/rcon.

Keep the password out of the command line, where every local user can read it with ps. rcon-cli reads it from a config file instead:

mkdir -p /etc/rust-wipe
cat > /etc/rust-wipe/rcon.yaml <<'YAML'
rust:
  address: "127.0.0.1:28016"
  password: "your-rcon-password"
  type: "web"
YAML
chmod 600 /etc/rust-wipe/rcon.yaml

Test it once by hand while the server runs:

rcon -c /etc/rust-wipe/rcon.yaml -e rust -t web "say [SERVER] RCON test"

The message should show up in game chat. Connect to 127.0.0.1 on the box itself and keep the RCON port closed in your firewall. If you would rather not install a client, set WARN_MINUTES="" in the script: the wipe still works, players just get no notice. The script also skips the warnings on its own when /usr/local/bin/rcon is missing.

The wipe script

Install the tools the script uses, then save it as /usr/local/bin/rust-wipe and make it executable with chmod 755 /usr/local/bin/rust-wipe:

apt-get install -y curl jq unzip
#!/usr/bin/env bash
# rust-wipe: stop, wipe, optionally update, start.
# Usage: rust-wipe map|full [forced]
set -euo pipefail

SERVICE=rust-server
RUST_DIR=/opt/rust-servers/instance-1
IDENTITY=my_server
STEAMCMD=/opt/rust-servers/steamcmd/steamcmd.sh
NEW_SEED=yes            # yes = random seed every wipe, no = keep the current map seed
OXIDE=no                # yes = reinstall Oxide after the forced wipe update
WARN_MINUTES="10 5 1"   # chat warnings before the stop, "" to skip

MODE=${1:-}
FORCED=${2:-}
ID_DIR="$RUST_DIR/server/$IDENTITY"
CFG="$ID_DIR/cfg/server.cfg"
STAMP="/var/lib/rust-wipe/forced-$(date +%Y-%m)"

log() { echo "rust-wipe: $*"; }
installed_build() {
  awk -F'"' '/"buildid"/ {print $4; exit}' "$RUST_DIR/steamapps/appmanifest_258550.acf"
}
rcon() {
  /usr/local/bin/rcon -c /etc/rust-wipe/rcon.yaml -e rust -t web "$1" >/dev/null 2>&1 || true
}

case "$MODE" in map|full) ;; *) echo "usage: rust-wipe map|full [forced]" >&2; exit 2 ;; esac
[ -d "$ID_DIR" ] || { log "no identity folder at $ID_DIR"; exit 1; }

# Forced wipe day: run once a month, and only after the new build is public.
if [ "$FORCED" = forced ]; then
  [ -e "$STAMP" ] && { log "forced wipe already done this month"; exit 0; }
  latest=$(curl -fsS https://api.steamcmd.net/v1/info/258550 \
    | jq -r '.data."258550".depots.branches.public.buildid') || latest=""
  if [ -z "$latest" ] || [ "$latest" = null ] || [ "$latest" = "$(installed_build)" ]; then
    log "no new public build yet, the next timer run will check again"
    exit 0
  fi
fi

# 1. Warn players, then save.
if [ -n "$WARN_MINUTES" ] && [ -x /usr/local/bin/rcon ] && systemctl is-active --quiet "$SERVICE"; then
  prev=""
  for m in $WARN_MINUTES; do
    if [ -n "$prev" ]; then sleep $(( (prev - m) * 60 )); fi
    rcon "say [SERVER] Wipe in $m minute(s). Find a safe spot!"
    prev=$m
  done
  sleep $(( prev * 60 ))
  rcon "server.save"
  sleep 2
fi

# 2. Stop and wait until the unit is really down. From here on, any exit starts it again.
systemctl stop "$SERVICE"
trap 'systemctl start "$SERVICE"' EXIT
for _ in $(seq 60); do
  case "$(systemctl show -p ActiveState --value "$SERVICE")" in inactive|failed) break ;; esac
  sleep 2
done
case "$(systemctl show -p ActiveState --value "$SERVICE")" in
  inactive|failed) ;;
  *) log "server did not stop, nothing deleted"; exit 1 ;;
esac

# 3. Forced wipe day: update the game first, then Oxide.
if [ "$FORCED" = forced ]; then
  update() { "$STEAMCMD" +force_install_dir "$RUST_DIR" +login anonymous +app_update 258550 validate +quit; }
  update || update
  [ "$(installed_build)" = "$latest" ] || { log "update did not reach build $latest"; exit 1; }
  if [ "$OXIDE" = yes ]; then
    curl -fsSL -o /tmp/oxide.zip https://github.com/OxideMod/Oxide.Rust/releases/latest/download/Oxide.Rust-linux.zip
    unzip -o -q /tmp/oxide.zip -d "$RUST_DIR"
    rm -f /tmp/oxide.zip
  fi
fi

# 4. Delete the map (and blueprints for a full wipe).
cd "$ID_DIR"
rm -fv -- *.map *.sav *.sav.[0-9]* *.sav.new *.navmesh *.navmesh.new *_occlusion_*.dat sv.files.*.db*
if [ "$MODE" = full ]; then
  rm -fv -- player.blueprints.*.db* player.states.*.db*
fi

# 5. Optional new seed, always greater than 0.
if [ "$NEW_SEED" = yes ]; then
  seed=$(shuf -i 1-2147483647 -n 1)
  if grep -q '^server\.seed ' "$CFG"; then
    sed -i "s/^server\.seed .*/server.seed $seed/" "$CFG"
  else
    echo "server.seed $seed" >> "$CFG"
  fi
  log "new seed $seed"
fi

if [ "$FORCED" = forced ]; then mkdir -p "$(dirname "$STAMP")"; touch "$STAMP"; fi
log "$MODE wipe done, starting $SERVICE"
# 6. The EXIT trap starts the server.

What it does, in order:

  1. Checks the arguments. map or full picks the wipe type. The optional second argument forced turns on the forced wipe day steps.
  2. Warns and saves. If the server is up and rcon-cli is installed, it posts Wipe in 10 minute(s), then 5, then 1, waits the last minute and sends server.save. The whole warning takes as long as the first number in WARN_MINUTES.
  3. Stops and waits. systemctl stop and then up to two minutes of polling until the unit is inactive or failed. If it never gets there, the script deletes nothing and exits.
  4. Updates on forced wipe day (next section).
  5. Deletes the files from the table above. rm -f ignores patterns that match nothing, so a missing file is not an error.
  6. Writes a new seed if NEW_SEED=yes: a random number from 1 to 2147483647, replacing the server.seed line in server.cfg or appending one. shuf never returns 0 in that range. Seed 0 is the one value to avoid: Panelra treats 0 or any negative seed as invalid, never writes it to server.cfg and uses a random positive seed instead.
  7. Starts the server. The trap ... EXIT runs systemctl start whenever the script ends after the stop, whether the wipe worked or an error cut it short, so a failed run never leaves the server down.

Try it once by hand before you schedule it, ideally on a test identity: rust-wipe map and watch journalctl -u rust-server -f while the new map generates.

Schedule it with a systemd timer

A timer is the better scheduler here. It can run on London time whatever time zone the host uses, it can say "first Thursday" directly, and every run lands in the journal. Create a oneshot service and a timer for your regular wipe:

# /etc/systemd/system/rust-wipe-weekly.service
[Unit]
Description=Weekly Rust map wipe

[Service]
Type=oneshot
ExecStart=/usr/local/bin/rust-wipe map
TimeoutStartSec=2h
# /etc/systemd/system/rust-wipe-weekly.timer
[Unit]
Description=Weekly Rust map wipe, not on forced wipe day

[Timer]
OnCalendar=Thu *-*-08..31 18:00:00 Europe/London

[Install]
WantedBy=timers.target

*-*-08..31 means any Thursday except the first of the month, so your weekly wipe never runs on the day Facepunch wipes anyway. The countdown starts at 18:00, so the server stops for the wipe about 10 minutes later. There is no Persistent=true on purpose: if the host was down at 18:00, you do not want a surprise wipe the moment it boots.

Enable it and check the next run times:

systemctl daemon-reload
systemctl enable --now rust-wipe-weekly.timer
systemctl list-timers 'rust-wipe*'
systemd-analyze calendar --iterations=3 "Thu *-*-08..31 18:00:00 Europe/London"

For a wipe every two weeks, one easy way is two dates per month, for example Thu *-*-15..21 18:00:00 Europe/London for the third Thursday, alongside the forced wipe on the first. For a monthly blueprint wipe outside forced day, add a second service that runs rust-wipe full.

Handle forced wipe day

Facepunch releases the monthly update on the first Thursday of the month, and the new version wipes every server's map. The Facepunch wiki gives the time as 19:00 London time. That is 18:00 UTC while the UK is on British Summer Time, from the last Sunday of March to the last Sunday of October, and 19:00 UTC the rest of the year. The next two, worked out with systemd's own time zone data: Thursday 1 October 2026 at 18:00 UTC, and Thursday 5 November 2026 at 19:00 UTC, after the clocks go back on 25 October.

The patch is not always on time, and your old server cannot take players whose client already updated. So instead of wiping at a fixed minute, the forced timer starts at 19:00 London time and runs every 15 minutes until 22:45:

# /etc/systemd/system/rust-wipe-forced.service
[Unit]
Description=Rust forced wipe: update and wipe

[Service]
Type=oneshot
ExecStart=/usr/local/bin/rust-wipe map forced
TimeoutStartSec=2h
# /etc/systemd/system/rust-wipe-forced.timer
[Unit]
Description=Rust forced wipe checks, first Thursday of the month

[Timer]
OnCalendar=Thu *-*-01..07 19..22:00/15:00 Europe/London

[Install]
WantedBy=timers.target

Each run of rust-wipe map forced:

  • exits at once if the stamp file /var/lib/rust-wipe/forced-YYYY-MM shows this month is done,
  • asks api.steamcmd.net, a community mirror of Steam's app info, for the public branch build and compares it with the buildid in steamapps/appmanifest_258550.acf. Same build means the patch is not out yet: exit and try again in 15 minutes,
  • otherwise warns, stops, runs app_update 258550 validate (twice if the first SteamCMD run fails), checks that the installed build now matches, wipes, sets the stamp and starts.

A timer never starts a service that is still running, so a slow download is not interrupted by the next 15 minute tick. Use full forced in ExecStart if you also wipe blueprints on forced day. Panelra's own forced wipe handling starts polling for the new build at 16:00 UTC and checks every 90 seconds, which is why the window here is generous rather than tight.

Oxide and Carbon after the update

The update runs with validate, which puts back the vanilla game DLLs that Oxide patched. Set OXIDE=yes and the script reinstalls the latest Oxide.Rust-linux.zip after the update and before the start. The Oxide release has to be the one built for the new game version; if OxideMod has not published it yet, your plugins will not load until it is out. How to install Oxide on Linux covers how to check. Carbon needs no step here: it updates itself on boot once Rust is updated.

Cron instead of systemd

Cron works if you prefer it, with two catches. It runs in the host's time zone, so on a UTC host 19:00 London time is 0 18 in summer and 0 19 in winter and you have to change the line twice a year. And when both the day-of-month and day-of-week fields are set, cron runs when either matches, so 0 18 1-7 * 4 means "18:00 on the 1st to 7th, and on every Thursday", not "the first Thursday". Test the date in the command instead, and escape % because cron treats it as a newline:

# /etc/cron.d/rust-wipe (host in UTC, summer times)
0 17 * * 4 root [ "$(date +\%d)" -gt 7 ] && /usr/local/bin/rust-wipe map
*/15 18-21 * * 4 root [ "$(date +\%d)" -le 7 ] && flock -n /run/rust-wipe.lock /usr/local/bin/rust-wipe map forced

Cron also does not stop a second copy while the first is still running, which matters when a forced update takes longer than 15 minutes. That is what flock -n /run/rust-wipe.lock on the forced line is for: a run that finds the lock taken exits at once.

Pitfalls

  • The seed lives in two places. If your unit passes +server.seed on the command line, change it there too, or stop passing it and keep it only in server.cfg. Otherwise the new seed may never take effect.
  • A wipe is not a backup. Copy the whole identity folder somewhere else first. It is the only way back from wiping the wrong server.
  • Another tool starts the server mid-wipe. Anything that runs systemctl start between the stop and the delete makes Rust write its old world back. Do not point a restart script or watchdog at the same unit during wipe windows.
  • Oxide ahead of Rust, or behind. On forced wipe day the new Oxide build can arrive after the Rust patch. Check the server log for Oxide loading after the first start.
  • Time zones in custom schedules. Always put Europe/London, or your own zone, on the OnCalendar= line rather than relying on the host's clock setting.
  • Players plan around wipe times. Announce your schedule, not only the countdown. Our Rust wipe schedule lists upcoming forced wipe dates if you want to post them.

Let Panelra handle wipes

Panelra's wipe scheduling does all of this without a script: weekly, every two weeks, a custom cron schedule or specific dates, map-only or full, forced wipe days with the game update included, a new random seed, the current seed or a custom one, a 5, 15, 30 or 60 minute chat countdown you can cancel from the panel, an optional backup before the wipe and skip rules so a regular wipe does not land right before forced day. It runs the same order the script does: warn, save, stop and wait, delete, start.

Free during the open beta. Pricing will be announced before the beta ends.

Frequently asked questions

What time is the Rust forced wipe?
Facepunch's wiki gives 19:00 London time on the first Thursday of the month. That is 18:00 UTC while the UK is on British Summer Time (last Sunday of March to last Sunday of October) and 19:00 UTC in winter. The patch can land later than that, so check for the new build instead of trusting the clock.
Should I use cron or a systemd timer?
A systemd timer. It can run on London time whatever the host's time zone is, can express the first Thursday of the month directly, and logs every run to the journal. Cron works, but it runs in the host's time zone and cannot say first Thursday in one field.
Does a map wipe remove blueprints?
No. A map wipe deletes the map, the save and its backups, and the per-world caches, so players keep what they learned. A full wipe also deletes the player.blueprints and player.states databases. Player identities, tokens, clans and contacts stay in both cases.
Can the wipe script run while the server is up?
It stops the server itself before it deletes anything, and waits until systemd reports the unit inactive. Deleting save files under a running server does not work: the next autosave or the shutdown save writes the old world back.

Skip the manual work: install the Panelra agent

Wipes, updates, restarts, plugins and crash alerts for your Rust servers, from one dashboard. One install command on your Linux host, no inbound ports for the agent.

Free during the open beta. Pricing will be announced before the beta ends.