A small wrapper for running a vanilla Terraria dedicated server on Linux. Keeps the server in a detached tmux session, mirrors its console to a real logfile, and gives you commands that return output instead of making you scrape a terminal.
$ ./tsrv start
starting..... ok
status: running (tmux session 'terraria')
world: My World.wld
listen: 0.0.0.0:7777 up
players:
No players connected.
$ ./tsrv say "dinner in 10"
$ ./tsrv save
$ ./tsrv stop # sends 'exit' so the world actually savesNo mods, no TShock — this drives the TerrariaServer binary that ships with
your normal Steam copy, so vanilla clients connect normally.
Running a Terraria server usually means leaving a terminal open forever, or
writing your own tmux incantations. And the first time you kill it instead of
typing exit, you lose everything since the last save, because Terraria only
writes the world on a clean shutdown.
tsrv stop sends exit and waits for the save to finish. That's most of the
point. (If the server is wedged and hasn't exited after 120s it force-kills
and tells you so — at that point progress since the last save is already
gone either way.)
Requires bash 4+, tmux, and a Terraria install (Steam or otherwise).
zstd if you want backup.sh; python3 for wldinfo.py.
sudo pacman -S tmux zstd # Arch
sudo apt install tmux zstd # Debian/Ubuntugit clone https://github.com/Solteris-Dev/tsrv.git
cd tsrv
cp serverconfig.example.txt serverconfig.txt # edit: world name, password, size
./tsrv startThe Terraria install is auto-detected — Steam's default location, flatpak, and
alternate library folders (it reads libraryfolders.vdf). Linux only: it
relies on ip, ss, tc and GNU grep -P. If your install is somewhere
unusual:
export TERRARIA_DIR=/path/to/Terraria| Command | What it does |
|---|---|
start |
Launch, wait until the server reports ready |
stop |
Send exit so the world saves, then wait (force-kills after 120s) |
restart |
Both |
status |
Running? listening? who's online? |
cmd "<x>" |
Run any console command, print its output |
say "<x>" |
Broadcast a chat message |
save |
Force a world save (blocks until finished) |
players |
List connected players |
log [n] |
Last n lines (default 40) |
chat [n] |
Just the in-game chat |
tail |
Follow the log |
attach |
Attach the tmux session (Ctrl-b d to detach) |
reply "<x>" |
Broadcast, auto-wrapped to chat-safe chunks |
logpath · gamedir · lanip · port |
Print paths/values, for scripting |
Exit codes: 0 ok, 1 error, 3 server not running. (3 applies to commands
that need a live server; log, chat and tail read the file and work either
way, and stop succeeds if it was already stopped.)
cmd waits for output to go quiet before returning, so a save — which runs
for several seconds — completes rather than bleeding into your next command.
Two things beyond starting the server:
1. Forward the port. Terraria uses TCP 7777 by default (whatever port=
says in your config). Forward it on your router to this machine's LAN address:
./tsrv lanip # the address to point the forward at
./tsrv port # the port to forwardGive a DHCP reservation to that address, or the forward breaks silently when the lease rotates and players just can't connect.
2. Tell players your public address, not the LAN one:
curl -s https://api.ipify.orgIf that address changes, look at dynamic DNS. If it's in 100.64.0.0/10 you're
behind CGNAT and port forwarding won't work at all — you'd need a tunnel or a
VPS relay.
Set a password in serverconfig.txt. Open Terraria servers do get scanned.
Terraria has this built in. Add one line to serverconfig.txt and friends can
join from their Steam list with no forwarding, no tunnel, and nothing to install:
lobby=friends # or: lobby=private
tsrv passes it through as -lobby. Needs the server running somewhere Steam
is available.
If that doesn't fit, playit.gg is the usual tunnel for game
servers — free, works behind CGNAT, gives players a host:port. Cloudflare
Tunnel is not a good fit here: the free tier is HTTP-oriented and raw TCP
would require every player to install cloudflared.
For a dynamic IP with working port forwarding, use ddclient, inadyn, or your
router's built-in dynamic DNS.
The server lives in a tmux session, so it survives you closing the terminal, but not a reboot. For that, a user systemd unit:
# ~/.config/systemd/user/terraria.service
[Unit]
Description=Terraria dedicated server
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=%h/tsrv/tsrv start
ExecStop=%h/tsrv/tsrv stop
TimeoutStopSec=180
[Install]
WantedBy=default.targetsystemctl --user daemon-reload
systemctl --user enable --now terraria.service
sudo loginctl enable-linger "$USER" # so it runs when you're not logged inTimeoutStopSec=180 matters — the world save on shutdown needs longer than
systemd's default, and being killed mid-save is how worlds get corrupted.
Everything the console prints is mirrored to logs/server.log with terminal
escapes stripped, so you can grep and tail it like any log. cmd returns
the response for a single command. That combination makes tsrv easy to drive
from a script, a cron job, or an AI agent.
If you want an AI agent in your Terraria chat specifically, that's a separate project: terraria-beyond-sandboxing.
| Script | Purpose |
|---|---|
backup.sh |
Timestamped .tar.zst of ./worlds plus this machine's native-Linux characters. Run tsrv save first — it copies live files and does not stop the server. |
wldinfo.py |
Read size / game mode / world evil out of any .wld |
gen-world.sh |
Generate worlds until one rolls Corruption (see below) |
joinwatch.sh |
Timestamped join/leave log — the console log has no timestamps |
netwatch.sh |
Ping a player's endpoint on a schedule, to tell a network drop from a client crash |
teardown.sh |
Guided backup-then-remove |
The dedicated server's autocreate can't choose — unlike the in-game world
creation menu, it rolls randomly. gen-world.sh generates in a loop, reads the
result with wldinfo.py, and keeps going until it gets the one you want.
GEN_WORLD_FILE="$PWD/worlds/My World.wld" ./gen-world.shWorth knowing before you plan backups: in Terraria the world lives on the
server but characters (.plr) live on each player's own machine. Nothing
running here can back up your friends' characters. They have to do it
themselves.
If remote players see ping spikes into the hundreds whenever anything on your box uploads, that's bufferbloat, not bandwidth. See docs/bufferbloat.md — on one 2.2 Mbit uplink it took worst-case latency under load from 527 ms to 28 ms.
Set TSRV_CHECK_SHAPING=1 to have tsrv start warn when no queue management
is active.
| Variable | Default |
|---|---|
TERRARIA_DIR |
auto-detected |
TSRV_SESSION |
terraria |
TSRV_CONFIG |
serverconfig.txt beside the script |
TSRV_PORT |
read from the config, else 7777 |
TSRV_IFACE |
default-route interface |
TSRV_CHECK_SHAPING |
0 |
tsrv will likely split into Terraria-specific utilities and a general
game-server harness. See docs/roadmap.md.
MIT.