# Running a Factorio server with Factorio Server Manager and Docker

> One container, one compose file, and a web UI for mods, saves, and restarts. From a box that has Docker to a factory your friends can join.

- date: 2026-08-30
- tags: factorio, docker, compose

The Factorio server behind this site is one container and one compose file.
Factorio Server Manager runs inside it: a web UI that downloads the headless server,
manages saves and mods, and starts and stops the game process. Server administration
is a browser tab.

Credit where it is due:
[Factorio Server Manager is Tricade's work](https://github.com/Tricade/factorio-server-manager),
carrying on the
[OpenFactorioServerManager](https://github.com/OpenFactorioServerManager/factorio-server-manager)
project. Everything pleasant about running this server - the mod portal login, the
save management, the graceful shutdown - is their code. Star the repo if you run a
factory.

Here's the full path, from a box that has Docker to a factory your friends can join.

## 1. Write the compose file

This is the whole deployment:

```yaml
services:
  manager:
    image: ghcr.io/tricade/factorio-server-manager:0.17.5
    restart: unless-stopped
    stop_grace_period: 3m
    environment:
      FACTORIO_VERSION: stable
      FSM_COOKIE_SECURE: "true"
    ports:
      - "34197:34197/udp"
    volumes:
      - fsm-data:/opt/fsm-data
      - factorio-data:/opt/factorio

volumes:
  fsm-data:
  factorio-data:
```

Every line that is not boilerplate earned its place:

- **Pin the image to an exact release tag.** The manager only changes when you decide
  it does.
- **Named volumes.** `factorio-data` holds the game and saves, `fsm-data` holds the
  manager state. Explicit names keep the compose project prefix out, so a backup job
  can address them directly.
- **`FSM_COOKIE_SECURE: "true"`**, because the UI will only ever be served over HTTPS.

> ⚠️ **Do not skip `stop_grace_period: 3m`.** On SIGTERM the manager asks Factorio for
> a clean save-and-stop and needs up to 165 seconds. The Docker default of 10 seconds
> kills the game mid-save, which is how maps die.

Then bring it up:

```bash
docker compose up -d
```

## 2. Point DNS at it - carefully

The UI rides your reverse proxy over HTTPS like any other web app. Game traffic does
not: it is UDP on 34197, and an HTTP proxy like Traefik cannot route it. That's why
the compose file publishes the port straight on the host.

It also means the DNS record for the game **must be a DNS-only A record** pointing at
the machine.

> ⚠️ **A CNAME to a proxied hostname resolves to the Cloudflare edge**, which accepts
> your packets and tells no one. If the UI loads fine but nobody can join, this is the
> first thing to check.

If a firewall fronts the box, open the game port explicitly:

```bash
sudo ufw allow 34197/udp
```

## 3. Log in and burn the bootstrap credential

The manager generates an admin credential on first start and writes it into the
volume. Read it out:

```bash
docker compose exec manager cat /opt/fsm-data/initial-admin-password.txt
```

Log in with it, change the password, and the file deletes itself. Until you do, the
bootstrap credential is lying around - do this first, not eventually.

## 4. Install the game and pin the version

From the UI, download the headless server and create (or upload) a save. Version
pinning matters twice here:

- The **manager image** is pinned to an exact release tag in the compose file.
- The **Factorio version** is pinned in the UI after install, because the server
  version must match the players' clients. `stable` in the compose file is only the
  first-install default.

Mods, save uploads, and restarts all happen in the UI from here on. The compose file
changes only when the manager itself gets an upgrade, which is exactly how often I
want to think about it.

## 5. Back up the volumes

Because the volumes have explicit names, a backup is one command with no compose
context needed:

```bash
docker run --rm -v factorio-data:/data -v "$PWD":/backup \
  alpine tar czf /backup/factorio-data.tgz -C /data .
```

Run it on a schedule, and test a restore once before you trust it.

## Verify

```bash
docker compose ps        # ✅ the manager container is up
ss -lun | grep 34197     # ✅ the game port listens on UDP
```

Then join from a Factorio client: **Multiplayer → Connect to address** with
`your-host:34197`. If the UI works but the join times out, go back to step 2 - it's
almost always the proxied DNS record.