Blog

Running a Terraria Multiplayer Server with systemd

Running the official Terraria dedicated server under systemd on a home server

Terraria co-op is lighter than Minecraft — a couple of friends hopping on for a weekend of digging doesn't need a beefy host. What actually drags things down is keeping the process alive: someone runs ./TerrariaServer in the foreground over SSH and the server dies with the session; someone else uses screen and forgets to reattach after a reboot. The real goal should be auto-start on boot, auto-restart on crash, and backups you can script. Here's the official dedicated server wrapped in a properly-written systemd unit, with each section explained in terms of what it's protecting.

Before the unit: user, directory, and world file

Put the SteamCMD or official dedicated server package somewhere like /opt/terraria. Create a dedicated system user terraria and give it ownership of its home and data directories — don't run a game server as root. In serverconfig.txt, spell out the world path and world name, maxplayers, the port (default 7777), and a password if you need one; autocreate only matters the first time the world is generated. Prefer absolute paths everywhere to minimize guesswork between WorkingDirectory and the config file.

The world file *.wld is your core asset — as important as the binary and config, but more fragile, because copying it mid-run can leave you with a half-written file. Before you even write the unit file, make sure the path is stable, the user can write to it, and the firewall rules (TCP/UDP per the official docs) are open. If you're mapping the port publicly, don't use 123456 as the password — the port scanners in your logs never take a day off. Start the server once manually as the terraria user before writing the systemd unit; that splits "permissions are wrong" from "my unit is wrong" into two problems you can debug separately.

[Unit]: when is it considered "startable"

[Unit]
Description=Terraria Dedicated Server
Documentation=file:/opt/terraria/serverconfig.txt
After=network-online.target
Wants=network-online.target

After=network-online.target is more solid than plain network.target: it avoids the bind failing while the network stack isn't ready, then having to rely on Restart to spin uselessly. Description shows up in systemctl status, so write it clearly to avoid confusion with other game servers on the host. Don't add a Requires= tying it to a database or anything here — the vanilla Terraria dedicated server has no such dependency, and adding one just creates a fake failure point.

[Service]: process model and crash policy

[Service]
Type=simple
User=terraria
Group=terraria
WorkingDirectory=/opt/terraria
ExecStart=/opt/terraria/TerrariaServer -config /opt/terraria/serverconfig.txt
Restart=on-failure
RestartSec=15
Nice=5
LimitNOFILE=8192

# optional: keep stdout in the journal
StandardOutput=journal
StandardError=journal

Type=simple fits a dedicated server that stays in the foreground and doesn't double-fork. WorkingDirectory is mandatory — otherwise relative world and log paths land somewhere you didn't expect. Restart=on-failure brings the process back when it exits non-zero; a manual systemctl stop won't get fought over by endless restarts. RestartSec=15 gives a crash room to breathe, so a config mistake doesn't peg the CPU with a restart loop.

Nice=5 yields a little CPU, useful when more critical services share the box. The interactive console is awkward under systemd; the standard way to change config is: stop the server → edit serverconfig.txt → start again. Fine for a small group. If you're heavily dependent on console commands, you'd need a wrapper or an rcon-style solution, which most vanilla-server setups never need.

Don't wrap ExecStart in a pointless shell loop to "implement your own daemon" — that's grabbing systemd's job, and signals and exit codes get messy.

[Install] and how to enable it

[Install]
WantedBy=multi-user.target

After systemctl daemon-reload && systemctl enable --now terraria.service, the server comes up once the multi-user target is reached. Status at a glance:

systemctl status terraria.service
journalctl -u terraria.service -f

Logs going to journald means one unified rotation policy. If the server also writes its own file logs, remember permissions and disk quotas.

Hooking backups into the unit's lifecycle

Running cp *.wld while the server is live risks catching a half-written file. The script should go through the unit explicitly:

  1. Announce in-game (if anyone's online)
  2. systemctl stop terraria.service
  3. Copy the world, serverconfig.txt, and the mod list to a backup disk / restic
  4. systemctl start terraria.service

Keep multiple versions; a copy on the same disk doesn't survive a disk failure. Go offsite, or at least onto a second medium. Keep tModLoader and vanilla in separate directories with separate units, and don't mix worlds; export the mod list so a new machine installs from the list — avoid the "I can join but you can't" problem.

The ops surface you agree on with friends

Who updates major versions, whether passwords rotate, who has SSH and systemctl access — write it all into one shared note. The unit file itself can go into git (minus passwords); on a new machine, daemon-reload restores the whole hosting setup.

Give the unit a "maintenance window" habit too: systemctl stop before upgrading the binary, replace files and verify ownership and permissions, then start. If the new version crashes on startup, journalctl -u terraria.service -n 100 usually exposes a missing library or path problem before the in-game error does. When you need temporary foreground debugging, systemctl stop frees the port, then run the same ExecStart manually as the terraria user and hand it back to systemd once confirmed — that way two sets of launch parameters never drift apart.

Firewall and port mapping go in the same note: TCP/UDP on 7777, whether it's only open to the VPN subnet, where the router forwarding-rule screenshot lives. When exposed publicly, spell out the password rotation cadence — more reliable than an oral agreement in the group chat.

A Terraria server isn't that complex. Handing the process model to systemd and treating the world as data to back up already puts you ahead of most "casual weekend room" setups. What actually makes it effortless isn't a fancier panel — it's status reading active, a backup script that stops cleanly, and a server that comes right back up.

← Back to Blog