Blog

Factorio Multiplayer and Mod Sync: The Server Is the Single Source of Truth

A Factorio multiplayer server running headless on a home Linux box

I burned an entire evening before our June season opener: six players, each running their own client setup, one person with an extra quality-related mod enabled, and everyone hard-stuck on mods mismatch. Factorio is ruthlessly strict about everyone being identical. So consider this my multiplayer logistics memo: who syncs what, how the server stays the authority, how to freeze versions, and how to rotate saves. The goal is a pinned checklist before the next opening night — not another voice-chat argument that runs past midnight.

Server baseline

The official headless package running under systemd, as its own unprivileged user. server-settings.json pins the server name, password, autosave interval, and max player count. The firewall only exposes the game port. A crash can restart, but you don't want a bad config causing an infinite crash loop that fills the log disk.

Save and mod directories are writable only by the service user — don't run the server as root. The data directory goes into the regular backups (local rotation plus a cold copy to the NAS), and I do a manual double backup before any big version bump or major factory rework. Updating headless follows the same flow as any other game server: stop, back up, update, watch the logs, admin joins first. Once ports and processes live under systemd, start troubleshooting from the unit status and the log timeline before you blame the mods.

Multiplayer is not a linear scale-up of the single-player sandbox: every extra player adds one more "is the client in sync with the server?" unknown. Writing the sync process down saves more wasted pre-launch time than buying a beefier box.

Logistics principle: the server decides

In multiplayer, the real "source" is three things on the server:

  • The mods/ directory (the zip for each mod)
  • mod-list.json (the enabled set)
  • The map save itself

Clients have to line up with the same set. Saying "everyone install the Space Age stuff" in voice chat will drift — someone grabs optional packages off the dependency chain, someone's a minor version behind, someone ticks one more "looks harmless" UI mod in the client. Our sync pipeline became:

  1. The admin tunes the mod combo on a test machine, opens a local map, and verifies it loads — no broken tech tree, no exploded recipes.
  2. Export mods/ and mod-list.json with a version manifest (name, version, hash — pick one).
  3. Upload to the server, stop it, swap the files in.
  4. Post the manifest in the group chat, or drop it in a small private git repo as the single source of truth.
  5. Everyone enables exactly what the manifest says, then joins.

A sync folder works fine as transport, but the server's files are the version of record — the sync drive is just the delivery layer. When someone can't get in, the first question is "does your list match the server manifest item by item?", not "must be a network problem."

The mismatch incident, live

Player A: enabled one extra "harmless, client-only" mod
Player B: mod at 1.2.3 vs server at 1.2.1
Player C: disabled a QoL mod the server has enabled
Result: nobody can join; logs only say mods inconsistent

The fix wasn't exotic: paste the server's mod-list.json in the group, have everyone reset to match the server, and ban "let me just try this one thing." After opening night, you enter the version freeze period: if a mod really needs upgrading, agree on a time, back up the saves first, and everyone updates at once. A freeze isn't rigidity — it's concentrating the change cost into a single window instead of one player half-upgrading during a work break and everyone rolling back at night.

If someone insists on testing a new mod, the right move is a separate test save or test server; validate there first, then run it through the official manifest process. The production save is not an experiment bed. The dependency chain belongs in the manifest too: listing only the top-level mod names without transitive dependency versions will still silently drift on the next update. We eventually kept both the "enabled list" and "zip filenames containing versions" in the repo, and the argument cost dropped immediately.

Save rotation and "logistics nodes"

Keeping N autosaves is usually not enough:

  • Daily: rolling autosaves, so a single crash isn't fatal.
  • Milestones: manual names, e.g. before-megabase-2026-06-xx.zip.
  • Before big factory reworks, base relocations, or mass blueprinting: one more manual save.

When the save is corrupt, the main bus gets torn down by accident, or a diggy-style disaster hits, you need "yesterday" or "before the change" — not the one latest file. Blueprint books and maps for trading go in a drive or a repo, not buried in chat history — that's the second easiest thing to lose in multiplayer logistics; the third is "which version did we actually have enabled back then."

Performance and ticks: remote players are part of the logistics

As the factory grows, the bottleneck is almost always single-core CPU and UPS (updates per second), not bandwidth. With few players, decorative mods, excessive dynamic effects, unconstrained circuits, and giant robot swarms will blow through the tick budget first. High-latency remote players need the server tick to stay stable far more than they need three more UI mods.

Monitor with the built-in stats or by watching process CPU externally; when UPS clearly drops, cut load: entity count, script frequency, update cadence. Don't pile heavy mods into the sync manifest while ticks are already in trouble. Space out map exchanges and big blueprint pastes to avoid everyone hitting the same logic spike at once. When remote players are in the mix, keep the server logic stable first — "one more convenient building mod" can wait. Bad experiences come from dropped ticks, not from a missing button.

Update-night checklist (multiplayer edition)

  1. Announce the maintenance window; say whether mods are changing.
  2. Double-backup the saves (a milestone on top of the rolling ones).
  3. Update headless and mods, strictly against the manifest.
  4. After startup, watch the logs; the admin joins first to verify.
  5. Post the current manifest's version table or hashes in the group.
  6. Only after everyone confirms they match, yell "factory time."

Factorio fun is built on sync discipline — the same muscle as ops: a clear authority source, freezable versions, rollbackable saves. Pinning the manifest repo link saves more grief than opening another "it's just a small tweak tonight" session.

One operations note to finish: keep a pinned group message with the "current server version and mod manifest" and have new joiners align before entering, rather than screenshotting logs after a failed join. Multiplayer logistics isn't about how fast the file drive syncs — it's about nobody arguing over who's the authority.

← Back to Blog