Game Hosting home

Run a Rust Server in Docker with Persistent Saves: Test the Restore

Run a Rust server with Docker Compose, preserve its identity, publish separate UDP ports, and test a graceful stop and restore before friends join.

Difficulty
HardDifficulty Hard
Time to do
30 min
Reading
9 min
Updated
By
Game Hosting team

You will need

  • Linux Docker Engine with Docker Compose
  • Shell access to the Docker host and its firewall
  • A Rust PC client and account for your own join check
  • A private secret for the server RCON password

A Rust server Docker image can launch correctly and still lose the world if it writes identity data to the disposable container layer. This guide shows you how to inspect an image, keep the complete server identity, separate the game and query ports, test a clean stop, and restore a stopped copy before your friends build on it. It covers the Rust PC dedicated server. Docker’s port table or a running process alone does not prove that another player can join.

Choose a Rust server Docker image you can inspect#

Treat a container image as a program another publisher runs on your machine. Before using it with your Rust server, inspect who publishes it, where its source lives, how the image is built, what command starts it, which user runs RustDedicated, and what the stop handler does. A latest tag can change; prefer a reviewed tag or digest and record it with your server notes.

Facepunch distributes the dedicated server through SteamCMD as App ID 258550; its server setup guide does not certify a third-party Docker image. For the example below, I inspected the public Didstopia Rust server source, its startup and shutdown scripts, and its Docker Hub image. It is a community image, not an official Facepunch image. Review its current source and release history yourself before using it; don’t copy an image merely because its README has a Compose file.

The test here used digest sha256:c60a3ab044eb69019616951087834468aed478a11b449391376b23db05ddc38a. The wrapper accepted SIGTERM, called WebRCON to save and quit, then ran the server process as an unprivileged docker user. It also ran SteamCMD to update Rust App ID 258550 at startup, so pinning this digest pins the wrapper and base files, not the game build. That is evidence about one image and configuration, not a continuing guarantee for future images. Check the actual process user after launch; the image’s configured default user can differ from the Rust process user.

If you’re still choosing between a home PC, Linux host, and managed server, start with the complete Rust setup route. Docker adds an image publisher, storage mount, and signal-handling layer to the same server job.

Keep the Rust identity in a named volume#

A Docker volume preserves files only if the image writes them there. Rust selects a data directory using server.identity; Facepunch’s server data guide describes world maps, saves, player and blueprint databases, configuration, and Rust+ companion data under that identity. Database -wal sidecars can contain pending writes, so stop Rust before taking a copy of the directory.

For the Didstopia image, the documented server install and identity tree sit under /steamcmd/rust, and the identity folder is /steamcmd/rust/server/<identity>. A named volume on /steamcmd/rust is easy to carry between replacement containers. A bind mount can be easier when host tools must read files directly, but its host path and file ownership must match the Docker daemon’s machine. With Docker Desktop, the daemon runs in its Linux VM, so a Windows folder path is not automatically the same path seen by the container.

Use the same volume target and the same server.identity after every recreation. docker compose down keeps named volumes by default; adding -v removes declared volumes and can erase the data you meant to preserve. An anonymous volume or a mount aimed at the wrong directory can also leave the real identity in the disposable container layer.

Set one identity and leave room for the workload#

Choose a short, stable identity such as friends. The launch setting server.identity selects the directory that holds the world. For a Windows vanilla two-process check of identity-to-save separation, see our Rust server identity guide; the container paths here still depend on this image’s mounts. Facepunch documents server/<identity>/cfg/server.cfg as the per-identity configuration file, and values in that file can override launch arguments. Inspect the image’s actual command and read back effective settings rather than assuming Compose environment variables won.

The current Facepunch setup requirements list 12 GB of free RAM and 15 GB of disk, with SSD or NVMe preferred. Those are general starting points, not a Docker memory limit or a tested guarantee for your map, plugins, and player count. Docker imposes no CPU or memory cap by default. First leave enough memory for Linux and the Rust process; then set container limits based on observed use and revisit them when the map or workload changes.

If you’d rather handle a Rust PC world through a game panel instead of maintaining an image and its update path, Game Hosting’s Rust PC service includes panel controls for settings and restarts plus three scheduled or manually created backup slots. Confirm written export and import terms before moving an existing identity; this Docker volume does not establish compatibility with another service.

Publish game and query traffic on their own UDP ports#

Rust’s client traffic and server-browser query both use UDP. Give each its own explicit mapping and keep the optional administration ports private:

Purpose Rust setting Protocol Docker Compose mapping
Player game traffic server.port UDP 28015:28015/udp
Browser query server.queryport UDP 28016:28016/udp
WebRCON administration rcon.port TCP No public mapping in this example
Rust+ companion service, if configured app.port TCP Publish only if your setup needs it

Compose defaults a published port to TCP unless /udp is written explicitly. Publishing a port also doesn’t configure the operating-system firewall, router, cloud firewall, or upstream NAT. Check each layer you control and test from the network your players will actually use. The query response can confirm the browser endpoint; it doesn’t prove the game join path.

Keep WebRCON off public interfaces. The image uses a separate TCP listener and an RCON password; putting its port in ports: makes it reachable on every host interface unless you bind it more narrowly. The example keeps the port inside Docker so maintenance commands can run through docker compose exec.

Start Rust with an explicit identity and storage path#

This Compose service pins the image digest used in the test, sets separate UDP ports, stores data in a named volume, and gives the image 90 seconds to stop cleanly. Keep the secret out of the Compose file: put it in a private .env beside the file, exclude .env from Git, and set its permissions for your host account.

yaml
services:
  rust:
    image: didstopia/rust-server@sha256:c60a3ab044eb69019616951087834468aed478a11b449391376b23db05ddc38a
    restart: unless-stopped
    stop_grace_period: 90s
    environment:
      RUST_SERVER_IDENTITY: friends
      RUST_SERVER_PORT: "28015"
      RUST_SERVER_QUERYPORT: "28016"
      RUST_SERVER_SEED: "${RUST_SERVER_SEED:?Set a seed in .env}"
      RUST_SERVER_WORLDSIZE: "${RUST_SERVER_WORLDSIZE:?Set a map size in .env}"
      RUST_SERVER_MAXPLAYERS: "${RUST_SERVER_MAXPLAYERS:?Set a player target in .env}"
      RUST_RCON_WEB: "1"
      RUST_RCON_PASSWORD: "${RUST_RCON_PASSWORD:?Set a private password in .env}"
    ports:
      - "28015:28015/udp"
      - "28016:28016/udp"
    volumes:
      - rust_server_data:/steamcmd/rust

volumes:
  rust_server_data:
    name: rust_server_data

Set the seed, world size, player target, and a long unique RCON password in .env; the example doesn’t imply that a particular map or slot count fits every group. Then review the fully expanded service and start it:

bash
chmod 600 .env
docker compose config
docker compose up -d rust
docker compose logs -f rust

Wait for Server startup complete. If the log instead repeats installation or stops before world loading, identify that error before adding restart loops. Check that the process is not running as root, query the effective server port with the image’s RCON command, and list the files under server/friends. If they appear elsewhere, stop and correct the mount before inviting anyone.

Publisher test note: I started this digest on a disposable Linux Docker engine, recreated it, restored a stopped copy, and received a Steam A2S query response through the mapped query port; I did not join with a Rust client or test a remote player’s route.

Recreate the container and confirm the same world loads#

The test that matters is replacement, not merely docker restart. Save, stop, recreate the container without removing the named volume, and inspect the log and identity before building on the world:

bash
docker compose exec rust rcon server.save
docker compose stop --timeout 90 rust
docker compose up -d --force-recreate rust
docker compose logs --tail 200 rust
docker compose exec rust rcon serverinfo

The wrapper must receive the stop signal, finish its save command, and let Rust quit before Docker’s grace period expires. In the test, the image logged Config Saved, shut down with quit, and exited 0; the replacement reported Loading World, restored entities from the save, and reached Server startup complete. A different image may ignore SIGTERM, use another identity path, or need a longer grace period. Docker sends SIGKILL after the timeout, which cannot run a final save.

Compare the map/save filenames and sizes under /steamcmd/rust/server/friends before and after. Ask Rust for server information and confirm it loaded the intended map. If it starts a blank identity, stop immediately: check server.identity, the volumes: target, and whether server.cfg overrides the startup values. Don’t let friends add progress until the saved identity reloads.

Restore a stopped copy to a separate test volume#

A named volume is still on the same Docker host, and it can be deleted independently from the container. For recovery, make a second copy only after Rust has stopped; then test that copy without overwriting the source. The example streams the stopped identity into another named volume. It includes cfg, map, save, databases, sidecars, and companion identity together.

bash
docker compose stop --timeout 90 rust
docker volume create rust_server_restore_test
docker run --rm \
  --mount type=volume,source=rust_server_data,target=/source,readonly \
  --mount type=volume,source=rust_server_restore_test,target=/restore \
  --entrypoint /bin/sh \
  didstopia/rust-server@sha256:c60a3ab044eb69019616951087834468aed478a11b449391376b23db05ddc38a \
  -c 'tar -C /source -cf - server/friends | tar -C /restore -xf -'

Before testing the restored copy, stop the normal server. A Rust+ companion.id must not be active in two simultaneous copies. Temporarily mount rust_server_restore_test at /steamcmd/rust and use the same server.identity, then start only that copy. Check that its logs load the existing save and that the identity files are present. The publisher test loaded 5,620 saved entities from the cloned volume. It generated no player-built structure, so this verifies the server save path rather than a player’s base.

If you need recovery after losing the Docker host, copy an archive to a separate machine or storage service and test it there. A volume on the same host is not off-host protection. For a more general decision between local setup routes, see how to host and verify a Rust server.

Quest complete

Before you invite your friends, check that the intended world loaded and that each player can connect. If those checks pass, you're ready for a session; if you'd rather not keep a PC on, that's what we're here for.

Quick answers

Will deleting a Rust container delete its world?

A named volume survives container removal unless you remove the volume too, but first confirm the chosen image stored the server identity in that volume.

Is a Docker volume a backup for a Rust server?

No. It survives container replacement on the same Docker host, but it does not give you an independent, tested recovery copy.

Are Rust game and query ports TCP or UDP?

Both the game and browser-query ports use UDP; keep RCON on its own private TCP path.

Does docker stop prove that Rust saved the world?

No. The image must handle the stop signal, have enough time to save and quit, and pass a recreate-and-load test.