# Why robtme/satisfactory-server keeps 8GB of game files outside the container

> A Dockerized Satisfactory dedicated server that stores its entire state in one host directory, with the game patching itself inside the image. The flags and environment variables are documented in detail, while the update path and the account rename leave gaps an operator has to close by hand.

**robtme/satisfactory-server** — A Dockerized version of the Satisfactory dedicated server

- Repository: https://github.com/robtme/satisfactory-server
- Website: https://hub.docker.com/r/robtme/satisfactory-server
- Stars: 2,078 · Forks: 187
- Language: Shell
- License: MIT
- Published: 2026-10-14 · Updated: 2026-10-14 · Language: en
- Canonical page: https://hysenlabs.com/projects/robtme-satisfactory-server

## Why game files are stored outside the container

robtme/satisfactory-server packages the Satisfactory dedicated server as a container image, and the build starts from steamcmd/steamcmd:ubuntu-22. That base already holds the SteamCMD download tooling, and the Dockerfile installs gosu, xdg-user-dirs, curl, jq and tzdata with apt-get, creates a steam user at UID 1000 and GID 1000, then copies the steamcmd directory out of root into that user's home. Game content therefore arrives from Steam's own downloader inside the container rather than from a vendored tarball.

Where those files sit is the design decision that makes a rebuild cheap. The README states they are stored outside the container 'to avoid needing to redownload 8GB+ every time you want to rebuild the container'. Everything that has to survive a rebuild lives under a single host directory bound to /config, so replacing the image costs a registry pull and a container restart. The code is Shell, and the repository is published under the MIT licence.

## Four directories under /config do four different jobs

One host directory, bound to /config, carries the whole server state. Backups live in /backups, and the server writes them automatically the first time the container starts. Game files live in /gamefiles so an image rebuild does not trigger another download. Logs live in /logs, which holds Steam's own log output plus a pointer to the game's logs, and empties on startup unless LOG=true is set. Saves, blueprints and server configuration live in /saved.

Ownership of that directory is the other half of the setup. The README asks you to run id on the host first and pass the result into the PGID and PUID environment variables, both usually 1000, and it names permission denied errors as the symptom when that step is skipped. The Dockerfile declares the same two values as ARG UID=1000 and ARG GID=1000, creates the steam group and user from them, and chowns /config to steam at build time, so a host account with a different id is the expected source of trouble.

## The first run is one docker run command

A bind mount alone is not enough, because Satisfactory needs two kinds of port, a user mapping and a memory ceiling. The README is explicit that the documented first run is a single command and that all of it should be copied.

```bash
docker run \
--detach \
--name=satisfactory-server \
--hostname satisfactory-server \
--restart unless-stopped \
--volume ./satisfactory-server:/config \
--env MAXPLAYERS=4 \
--env PGID=1000 \
--env PUID=1000 \
--env STEAMBETA=false \
--memory-reservation=4G \
--memory 8G \
--publish 7777:7777/tcp \
--publish 7777:7777/udp \
--publish 8888:8888/tcp \
robtme/satisfactory-server:latest
```

--volume ./satisfactory-server:/config is the bind described above. --memory-reservation=4G reserves 4GB of host RAM for the container and --memory 8G caps it at 8GB, a pair the README says may need raising as the save approaches the late game or when many players join. --restart unless-stopped brings the server back after a reboot, --name and --hostname both use satisfactory-server, and the README says to replace --detach with --sig-proxy=false when you want container logs in your terminal.

## Compose, cluster manifests and a third-party chart update differently

docker-compose.yml at the top level of the repository is a translation of the same flags into YAML: the same three published ports, the same ./satisfactory-server bind to /config, the same MAXPLAYERS=4, PGID=1000, PUID=1000 and STEAMBETA=false entries, restart unless-stopped, and the 4G reservation with the 8G limit nested under deploy: and resources: instead of being written as command line flags.

For clusters the repository ships cluster/service.yaml, cluster/statefulset.yaml and an example cluster/values.yaml. Where those stop, a chart maintained by someone else takes over: the README points at ArtifactHUB and installs the k8s-at-home chart for Satisfactory.

```bash
helm repo add k8s-at-home https://k8s-at-home.com/charts/
helm repo update
helm install satisfactory k8s-at-home/satisfactory -f values.yaml
```

That chart does not live in this repository, so pulling a new server image and upgrading the chart become two separate operations on two separate schedules, and the example values file in the cluster directory is the only configuration reference in the tree for either route.

## Port 7777 for game traffic and 8888 for messaging

Both published ports appear in the build file's EXPOSE line as 7777/udp 7777/tcp 8888/tcp, and each has an environment default behind it: SERVERGAMEPORT is 7777 and SERVERMESSAGINGPORT is 8888. Publishing 7777 twice, once for TCP and once for UDP, is not redundancy, since a run command that maps only one of the two leaves players unable to join.

MULTIHOME defaults to ::, a wildcard bind address, TIMEOUT defaults to 30 and MAXTICKRATE to 30 ticks. A server that feels sluggish under load therefore has documented knobs to turn, and the README does not say which one to reach for first.

The image also carries a health check of its own. The Dockerfile declares HEALTHCHECK --timeout=30s --start-period=300s CMD bash /home/steam/healthcheck.sh, and healthcheck.sh sits at the top level of the repository next to init.sh and run.sh. That 300 second start period has to absorb a first boot that pulls 8GB+ of game files before any check can fail the container.

## Every setting is an environment variable with a baked default

The Dockerfile sets defaults in a single ENV block, and every one of them can be overridden at run time with --env. AUTOSAVENUM defaults to 5, the count of rotating autosave files kept. MAXOBJECTS defaults to 2162688, an object limit only very large factories meet. MAXPLAYERS defaults to 4, STEAMAPPID is fixed at 1690800 and VMOVERRIDE defaults to false.

Two switches read against their own names. LOG defaults to false and the README's table describes it as disabling Satisfactory log pruning, so the value that preserves logs is LOG=true, which matches the /logs note that the directory empties on startup unless that is set. DISABLESEASONALEVENTS defaults to false and switches off the FICSMAS event when set. DEBUG defaults to false and exists for debugging the server itself.

Custom Steam branches use three variables as a set: STEAMBETA defaults to false, while STEAMBETAID and STEAMBETAKEY start as empty strings for you to fill in.

## The game patches itself, the image needs a pull

Two different updates are involved, and they have different triggers. The game updates whenever the container is started or restarted, unless you set SKIPUPDATE=true, so a new game build does not require a new image. Replacing the image itself means four commands.

```shell
docker pull robtme/satisfactory-server:latest
docker stop satisfactory-server
docker rm satisfactory-server
docker run ...
```

Under Docker Compose the same replacement takes two commands instead of four.

```shell
docker compose pull
docker compose up -d
```

Two limits are worth planning around before the first boot. The README says the server may run on less than 8GB of RAM while still recommending 8GB - 16GB per the official wiki, and it warns that the container's --memory restriction may need raising as the late game approaches or with many players, which means the example cap is a starting point rather than a ceiling you can ignore. TLS is the other rough edge: without Certbot, Satisfactory serves a self-signed certificate and every player confirms it manually on first connect, and the README points to the ssl directory for the Let's Encrypt setup and for known connectivity problems that appear after a certificate is issued. The README does not document a way back to an earlier image tag or an earlier game build.

## The badges still point at the pre-rename namespace

The README opens with a note that the author's account was renamed from wolveix to robtme, recommends robtme/satisfactory-server or ghcr.io/robtme/satisfactory-server, and says wolveix/satisfactory-server will keep receiving updates so existing setups do not break. Two Docker Hub repositories then carry the same image, and the badges at the top of the README still read from the old one: Docker Pulls, Docker Stars and Image Size all point at wolveix/satisfactory-server. Nothing further in the README says which namespace a new install should use, and both the run command and docker-compose.yml use the new name.

The repository listing also holds entries the README never explains. Two environment defaults disagree about where saves live: GAMECONFIGDIR is /config/gamefiles/FactoryGame/Saved, inside the bind mount, while GAMESAVESDIR is /home/steam/.config/Epic/FactoryGame/Saved/SaveGames, outside it, even though the README puts saves under the bind mount. The three shell files that would reconcile those paths, init.sh, run.sh and healthcheck.sh, are copied into the image and made executable, yet their contents are not part of the build file, so the route from an environment variable to the running server is not visible there. A saveshare directory and a server.log file also sit at the top level with no description. The last push was on 2026-10-06.

## Conclusion

Adopt robtme/satisfactory-server if you run a Docker host and are content to let the game patch itself on every restart. Skip it if you need a pinned, tested save schema or a documented rollback path, since the README does not describe rolling back to an earlier image tag and SKIPUPDATE=true only freezes the game build going forward. Before the first boot, run id on the host, pass the result as PUID and PGID, and decide whether the 8G memory cap in the example command is enough for your player count.

## FAQ

### How do I run robtme/satisfactory-server with Docker Compose?

The repository ships docker-compose.yml with the same three ports, the ./satisfactory-server bind to /config, the entries MAXPLAYERS=4, PGID=1000, PUID=1000 and STEAMBETA=false, restart unless-stopped, and a 4G reservation with an 8G limit under deploy:. Updating it means docker compose pull followed by docker compose up -d.

### Why does robtme/satisfactory-server report permission denied errors?

The README asks you to run id on the host and pass the result into the PGID and PUID environment variables, both usually 1000, because /config is chowned to the steam user inside the image. A host account with a different id is the documented cause of those errors.

### Does robtme/satisfactory-server update Satisfactory automatically?

Yes, the game updates when the container is started or restarted, unless you set SKIPUPDATE=true. The image itself is a separate matter and needs docker pull or docker compose pull, followed by recreating the container.

### Which ports does robtme/satisfactory-server need to expose?

7777 over both TCP and UDP for the game, taken from SERVERGAMEPORT, and 8888 over TCP for messaging, taken from SERVERMESSAGINGPORT. The build file exposes 7777/udp 7777/tcp 8888/tcp, and mapping only the TCP side of 7777 leaves players unable to connect.

### Does robtme/satisfactory-server give players an SSL certificate?

No. Without Certbot the game serves a self-signed certificate that each player confirms manually on first connect. The repository keeps the Let's Encrypt and Certbot instructions, plus notes on connectivity problems after a certificate is issued, in its ssl directory.

## Sources

- [License: MIT](https://github.com/robtme/satisfactory-server/blob/main/LICENSE)
- [Project website](https://hub.docker.com/r/robtme/satisfactory-server)
- [README](https://github.com/robtme/satisfactory-server/blob/main/README.md)
- [Releases](https://github.com/robtme/satisfactory-server/releases)
- [robtme/satisfactory-server on GitHub](https://github.com/robtme/satisfactory-server)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/robtme-satisfactory-server
