How to Back Up a Rust Server on Linux

Back up a Rust server on Linux: which files matter, saving first, a tar and zstd script, rotation, off-site copies with rclone and testing a restore.

Last updated Verified on Ubuntu 26.04.1 LTS, Rust build 25582902, GNU tar 1.35, zstd 1.5.7, 2026-09-28

On this page

A Rust server backup is the install directory minus the game files: the identity folder under server/ (map, save, player databases, cfg/) plus oxide/ or carbon/ for plugins, their configs and data. Save over RCON or stop the server first, pack it with tar --zstd, keep a rolling set on the host, copy it off the box with rclone, and restore one on a test box now and then so you know it works.

What to back up

Everything that makes your server yours lives in a handful of places inside the install directory. On our test host that is /opt/rust-servers/instance-1, with +server.identity set to a folder under server/.

PathWhat it holds
server/<identity>/*.map, *.savThe generated map and the world save: bases, deployables, loot. .sav.1 and .sav.2 are the server's own rolling copies of earlier saves.
server/<identity>/*.navmesh, *_occlusion_3.datNavmesh and occlusion data for the current map. The navmesh is written with every world save and read back on start, so treat it as part of the save.
server/<identity>/*.dbSQLite databases: player.*.db (blueprints, identities, deaths, player states, tokens), clans, relationship and sv.files (images on signs). Copy each one together with its -wal file.
server/<identity>/cfg/server.cfg, users.cfg (owners and moderators), bans.cfg.
oxide/Plugins (plugins/), their settings (config/), their saved data and Oxide's users and groups (data/), translations (lang/).
carbon/The same for Carbon: plugins/, configs/, data/, plus modules/.
HarmonyMods/Harmony mods, if you use any.

The wipe guide explains which of these a map or blueprint wipe deletes. See how to wipe a Rust server.

What to leave out

The game itself comes from SteamCMD and can be downloaded again with app_update 258550 validate, so it has no place in a backup. These are the top-level entries the Panelra agent excludes, with sizes from our test host:

EntrySize
Bundles/5.0 GB
RustDedicated_Data/708 MB
steamclient.so, UnityPlayer.so36 MB and 35 MB
libsteam_api.so, RustDedicated, runds.sh, steam_appid.txt, steamapps/under 1 MB together

Leaving these out turns a 6 GB archive into a few hundred megabytes. On the test host the remaining data was 570 MB, almost all of it in server/.

One more thing to skip: files named after a seed you no longer run. After a seed change the old .navmesh and _occlusion_3.dat files stay behind, and so do .sav.1/.sav.2 if the wipe deleted only *.map and *.sav. On our host the files from two old seeds took about 330 MB. Delete them while the server is stopped, as the wipe guide describes, and your backups shrink with them.

Save first, or stop the server

A running server holds the world in memory and writes it to disk only on an autosave (server.saveinterval, 600 seconds by default) or on shutdown. A backup taken between saves is up to ten minutes old, and a copy taken while the save is being written can be torn.

You have two options:

  • Hot backup. Send server.save over RCON and wait for Saving complete in the journal (on our host it follows within a second), then run tar. The Panelra agent does the same for its backups: server.save over RCON, a 2 second pause, then tar. Players keep playing, so a few seconds of changes can still land between the save and the copy.
  • Cold backup. Stop the service, confirm systemctl show -p ActiveState says inactive, copy, start. RustDedicated also saves on SIGTERM, but a hung server never gets there, so save first anyway. This is the copy to take before a wipe, an update or any manual file surgery.

Rust's RCON is WebSocket based, so you need a client that speaks it. The wipe automation guide sets up rcon-cli with a config file at /etc/rust-wipe/rcon.yaml; the script below reuses it.

A backup script with tar and zstd

GNU tar 1.35 on Ubuntu 26.04 has --zstd built in; it needs the zstd binary, which our test host already had (apt install zstd if yours does not). On the test host we packed the same data both ways: zstd took 4.3 seconds for 378 MB, gzip took 46.5 seconds for 382 MB. Both shrink the data by about a third, so pick the fast one.

Save this as /usr/local/bin/rust-backup.sh and make it executable:

#!/usr/bin/env bash
set -euo pipefail

RUST_DIR=/opt/rust-servers/instance-1
SERVICE=rust-instance-1
NAME=instance-1
DEST=/var/backups/rust      # never inside RUST_DIR
KEEP_DAYS=14
RCON="rcon -c /etc/rust-wipe/rcon.yaml -e rust -t web"

mkdir -p "$DEST"
OUT="$DEST/$NAME-$(date -u +%Y%m%d-%H%M%S).tar.zst"

# 1. Flush the world to disk if the server is up.
if systemctl is-active --quiet "$SERVICE" && [ -x /usr/local/bin/rcon ]; then
  $RCON "server.save" || echo "save failed, backing up the last autosave"
  sleep 5
fi

# 2. Pack everything except the game files. Exit 1 = a file changed while read.
set +e
nice -n 19 ionice -c3 tar --zstd -cf "$OUT.part" -C "$RUST_DIR" \
  --exclude=./Bundles --exclude=./RustDedicated_Data --exclude=./steamapps \
  --exclude=./UnityPlayer.so --exclude=./steamclient.so --exclude=./libsteam_api.so \
  --exclude=./RustDedicated --exclude=./steam_appid.txt --exclude=./runds.sh \
  .
rc=$?
set -e
if [ "$rc" -gt 1 ]; then rm -f "$OUT.part"; echo "tar failed ($rc)"; exit "$rc"; fi

# 3. Check the archive, then publish it under its final name with a checksum.
zstd -tq "$OUT.part"
mv "$OUT.part" "$OUT"
( cd "$DEST" && sha256sum "$(basename "$OUT")" > "$(basename "$OUT").sha256" )

# 4. Local rotation.
find "$DEST" -maxdepth 1 -name "$NAME-*.tar.zst*" -mmin +$(( KEEP_DAYS * 1440 )) -delete

nice and ionice -c3 keep the backup from stealing CPU and disk time from the game. The .part name means a half-written archive is never mistaken for a good one. If tar exits with 1, it printed which file changed while it was reading; that is normal for a hot backup, and the next run will catch it.

Run it every six hours with a systemd timer. /etc/systemd/system/rust-backup.service:

[Unit]
Description=Back up Rust server instance-1

[Service]
Type=oneshot
ExecStart=/usr/local/bin/rust-backup.sh

/etc/systemd/system/rust-backup.timer:

[Unit]
Description=Back up Rust server every 6 hours

[Timer]
OnCalendar=*-*-* 00/6:15:00
Persistent=true

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now rust-backup.timer
systemctl list-timers rust-backup.timer

Keep the backup times away from your restart and wipe times, so a backup never runs while the server is stopping or generating a map.

Rotation

At 378 MB per archive and four a day, 14 days of local backups is about 21 GB. Check that against df -h before you pick KEEP_DAYS, because a full disk will stop the server from saving. A plan that works for most community servers:

  • On the host: every six hours, kept for 3 to 14 days. This covers "a plugin ate the economy data yesterday".
  • Off site: every archive, kept for 30 to 60 days.
  • Before every wipe: one cold backup, kept until the next wipe at least. It is the only way back to the previous map if the wipe goes wrong.

Off-site copies with rclone

A backup on the same disk dies with the disk, or with the VPS when the provider suspends it. Copy the archives to any S3-compatible storage: Cloudflare R2, Backblaze B2, Hetzner Object Storage, Wasabi or AWS S3. rclone is in the Ubuntu archive (apt install rclone).

Create a bucket and an access key that can only read and write that bucket, then create the remote. This example is for Cloudflare R2; for other services change provider (Hetzner, Wasabi, AWS or Other) and endpoint:

rclone config create offsite s3 \
  provider=Cloudflare \
  access_key_id=YOUR_ACCESS_KEY_ID \
  secret_access_key=YOUR_SECRET_ACCESS_KEY \
  endpoint=https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com \
  acl=private

rclone obscures the secret in its config file, but the command stays in your shell history; run rclone config instead if you prefer the prompts. Then add these lines to the end of the backup script:

# 5. Off-site copy, then prove it arrived intact.
rclone copy "$DEST" offsite:rust-backups/$NAME --include "$NAME-*" --s3-no-check-bucket
rclone check "$DEST" offsite:rust-backups/$NAME --include "$NAME-*" --one-way

rclone copy uploads only new or changed files and never deletes anything at the destination, so local rotation does not touch the remote copies. --s3-no-check-bucket stops rclone from trying to create the bucket, which a key limited to one bucket is not allowed to do. rclone check --one-way compares the sizes and hashes of every local file with the remote copy.

Prune the remote once a day, and preview it first:

rclone delete offsite:rust-backups/instance-1 --min-age 60d --dry-run
rclone delete offsite:rust-backups/instance-1 --min-age 60d

Most providers can also expire objects with a bucket lifecycle rule, which keeps working even if the host is gone.

Restore a backup

  1. Pick and check the archive. cd /var/backups/rust && sha256sum -c instance-1-20260928-120015.tar.zst.sha256 must print OK. For an off-site copy, rclone copy it down first.
  2. Stop the server with systemctl stop rust-instance-1 and wait for ActiveState=inactive. Restoring under a running server is pointless: it writes its own world back on the next save.
  3. Move the current state aside instead of deleting it, so you can undo the restore:
    cd /opt/rust-servers/instance-1
    mkdir -p /var/backups/rust/pre-restore
    mv server oxide /var/backups/rust/pre-restore/   # or carbon, if you run Carbon
    
  4. Extract. The whole archive:
    tar --zstd -xf /var/backups/rust/instance-1-20260928-120015.tar.zst -C /opt/rust-servers/instance-1
    
    Or only part of it, for example just the plugin data, by naming paths as they appear in tar --zstd -tf: ./oxide/data.
  5. Check it will load. The identity folder must match +server.identity in the unit. The save is named proceduralmap.<size>.<seed>.<n>.sav, and server.worldsize and server.seed in server/<identity>/cfg/server.cfg must match it, as must the version number for the current Rust build, or the server will generate a new map next to it. If the unit runs as a non-root user, chown -R the restored files to that user.
  6. Start and watch journalctl -u rust-instance-1 -f. A good restore shows Spawning N entities from save, then Server startup complete. If there is no entities from save line and the world is empty in game, the server did not find your save: check step 5 again.

If you restore a Carbon install from before a Rust update, Carbon updates its own files on the first start. A restore never touches Oxide's DLLs: they live in RustDedicated_Data/Managed, which is not in the backup. On a fresh install, and after every Rust update, install Oxide as usual. See Oxide on Linux and Carbon on Linux.

Test a restore before you need it

A backup you have never restored is a guess. Once a month, and after any change to the script:

  • Extract the newest archive into a scratch directory with tar --zstd -xf ... -C /tmp/restore-test and check that server/<identity>/ has a .sav, the player.*.db files and cfg/server.cfg, and that oxide/data or carbon/data is there.
  • Better: install a second copy of the game with SteamCMD on a spare box, install Oxide there if you use it (its DLLs are not in the archive), extract the archive into it, give it different ports and start it. When it logs Spawning N entities from save, join and look at a base you know.
  • Pull one archive back from the remote and run sha256sum -c on it, so you know the off-site copies work too.

Panelra backups

Panelra does all of this for you: install the agent on your Linux host and you get manual backups from the dashboard, scheduled backups with a cron expression, and an optional automatic backup before every scheduled wipe. The agent saves over RCON, packs everything except the game files listed above, records a SHA-256 checksum and uploads the archive to cloud storage, so a dead host does not take the backups with it. Old backups are deleted after the number of days you set per server. With the pre-wipe backup turned on, a forced wipe waits up to 10 minutes for its backup and then goes ahead, so a slow upload never keeps your players on the old map.

Restore is one click on a stopped server. The agent checks free disk space, downloads the archive, verifies the checksum, takes a snapshot of the current files, cleans them, extracts the backup and checks that the identity folder is there. If any step fails, it rolls back to the snapshot. See backups and recovery.

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

Backup checklist

  • Back up server/<identity>/, oxide/ or carbon/, and HarmonyMods/.
  • Exclude Bundles/, RustDedicated_Data/, steamapps/ and the Steam and Unity libraries.
  • server.save over RCON before a hot backup; stop the server before wipes and updates.
  • Write to a .part file, test it with zstd -t, then rename and add a .sha256.
  • Rotate locally and watch free disk space.
  • Copy off site with rclone copy, confirm with rclone check --one-way.
  • Keep one cold backup from before each wipe.
  • Test a restore every month.

Frequently asked questions

Can I back up a Rust server while it is running?
Yes, if you send server.save over RCON first and wait for Saving complete in the log. The world is then on disk, but players keep changing it while tar reads, so the copy can still be a few seconds inconsistent. For a copy you can fully trust, stop the service first.
How big is a Rust server backup?
On our test host a 4000 size map with a Carbon install came to about 570 MB of data, of which the navmesh files are the largest part, and 378 MB compressed with zstd. The game files you should skip are about 5.8 GB and can be downloaded again with SteamCMD.
Do I need to back up the game files?
No. Bundles, RustDedicated_Data, the Unity and Steam libraries and steamapps come from SteamCMD with app_update 258550 validate. Backing them up only makes every archive more than ten times bigger.
Will an old backup load after a forced wipe update?
Not as a world. The save file names carry a version number (289 on the build we tested) that goes up with forced wipe updates. The server only loads files with its current number, so it ignores the old save and generates a fresh map. The player state, clans, relationship and sv.files databases carry the same number and are left behind too. Blueprints, identities, configs and plugin data from the old backup are still useful.

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.