initial commit

This commit is contained in:
adam committed 2026-09-28 16:52:30 +00:00
commit 910b438686
19 files changed
+1790

No files matched your search

+698
View File
@@ -0,0 +1,698 @@
# terraria-server-manager
Run any number of vanilla Terraria and tModLoader dedicated servers on one Linux host, each managed as its own systemd service, with the `terractl` command.
## Contents
1. [Overview](#overview)
2. [Components](#components)
3. [Directory layout](#directory-layout)
4. [Installation](#installation)
5. [Migrating from the manual install](#migrating-from-the-manual-install)
6. [Server zips](#server-zips)
7. [Creating an instance](#creating-an-instance)
8. [Starting, stopping and boot](#starting-stopping-and-boot)
9. [The server console](#the-server-console)
10. [Configuration](#configuration)
11. [Mods (tModLoader)](#mods-tmodloader)
12. [Versions and upgrades](#versions-and-upgrades)
13. [Deleting and restoring](#deleting-and-restoring)
14. [Backups](#backups)
15. [Permissions](#permissions)
16. [Running by hand for debugging](#running-by-hand-for-debugging)
17. [Troubleshooting](#troubleshooting)
18. [Command reference](#command-reference)
---
## Overview
Each server is an **instance** with a short name such as `survival` or `calamity`. An instance is:
- a directory, `/var/lib/terraria/instances/<name>/`, holding everything about that server;
- a systemd service, `terraria@<name>.service`, that runs it as the unprivileged `terraria` user.
Server software is never installed by hand. You drop the official zips into `/var/lib/terraria/dist`, and the first time an instance starts, the right zip is unpacked into that instance's `server/` folder. Worlds, mods and settings live separately in `data/`, so server software can be replaced or upgraded without touching them.
Each instance chooses between **vanilla** and **tModLoader** independently, so both kinds can run side by side on different ports.
---
## Components
Everything is installed by the `terraria-server-manager` package:
| File | Purpose |
|---|---|
| `/usr/lib/systemd/system/terraria@.service` | Template service. One copy runs per instance. |
| `/usr/lib/systemd/system/terraria@.socket` | Creates the console pipe the server reads commands from. |
| `/usr/bin/terractl` | Helper script. The service calls it to set up, run and stop servers; you use it for everyday management. |
| `/etc/default/terractl` | Optional overrides (locations, stop timeout). |
| `/usr/lib/sysusers.d/terractl.conf` | Defines the `terraria` system user. |
| `/usr/lib/tmpfiles.d/terractl.conf` | Creates `/var/lib/terraria` and its subdirectories. |
| `/usr/share/terraria-server-manager/export-tmod-mods.ps1` | Optional. Copy to your Windows PC to collect your enabled tModLoader mods. |
What happens when an instance starts:
1. systemd creates the console pipe `/run/terraria/<name>.stdin` (from the socket unit).
2. `terractl prepare <name>` creates the instance directory and default configs if they're missing, and unpacks the server zip if it hasn't been unpacked yet.
3. `terractl run <name>` starts the vanilla or tModLoader server, reading commands from the pipe and logging to the journal.
On stop, `terractl stop` sends `exit` through the pipe so the world is saved before the server shuts down.
---
## Directory layout
```
/var/lib/terraria/ Home of the 'terraria' user
├── dist/ Server zips (you add these; never modified)
│ ├── terraria-server-1458.zip
│ └── tModLoader-v2026.07.3.0.zip
└── instances/
├── <name>/ One directory per instance
│ ├── instance.conf Which server type and zip to use
│ ├── serverconfig.txt Terraria's own settings (port, world, password...)
│ ├── server/ Unpacked server software (replaceable)
│ │ └── .installed Records what was unpacked
│ └── data/ Your data (never touched by upgrades)
│ ├── Worlds/ World files
│ ├── Mods/ tModLoader mods + enabled.json
│ ├── ModConfigs/ tModLoader mod settings
│ └── banlist.txt
└── .deleted/ Backups made by 'terractl delete'
/run/terraria/<name>.stdin Console pipe (exists only while running)
```
The server can write only inside `instances/`. It reads `dist/` but can never change it.
---
## Installation
The package works on Debian 12 or newer and Ubuntu 22.04 or newer.
```bash
sudo apt install ./terraria-server-manager_2.0.0_all.deb
```
Using `apt install ./file.deb` (rather than `dpkg -i`) installs the dependencies automatically. `unzip` is required. `curl` and the ICU library are "recommended", which apt installs by default; they're only needed for tModLoader instances, which crash with `Aborted` without ICU.
Installing the package:
- creates the `terraria` system user and group;
- creates `/var/lib/terraria/`, `/var/lib/terraria/instances/` and `/var/lib/terraria/dist/`;
- installs the units and helper script.
It doesn't start or enable anything.
If you use a firewall, open each instance's port for **TCP**. Terraria uses TCP only.
```bash
sudo ufw allow 7777/tcp # example with ufw
```
### Upgrading the package
```bash
sudo apt install ./terraria-server-manager_<new-version>_all.deb
```
Running servers are not restarted. Each one picks up the new version the next time it starts. Your changes to `/etc/default/terractl` are kept; if the new package also changes that file, dpkg asks which version to keep.
### Removing the package
```bash
sudo apt remove terraria-server-manager # stops all instances (worlds are saved first)
sudo apt purge terraria-server-manager # also removes config and boot-time enablement
```
Neither command deletes `/var/lib/terraria` or the `terraria` user, so your worlds survive even a purge. To delete everything:
```bash
sudo rm -rf /var/lib/terraria && sudo userdel terraria
```
### Upgrading from the `terraria-instance` package
Versions before 2.0.0 were packaged as `terraria-instance`, and the command had the same name. The new package declares that it replaces the old one, so installing it removes `terraria-instance` automatically:
```bash
sudo apt install ./terraria-server-manager_2.0.0_all.deb
```
Things to know:
- **Instances are stopped (and saved) during the switch**, because removing the old package stops them. Start them again afterwards with `sudo systemctl start terraria@<name>`. Instances, worlds, mods and the `terraria` user all carry over unchanged.
- **The command is now `terractl`.** Update any scripts or cron jobs that call `terraria-instance`.
**Clean up the old package straight after upgrading.** The old package is removed, but dpkg still keeps a record of it because its settings file is left behind. Purging that record, including with a general cleanup like `apt purge ~c` or `apt autoremove --purge`, runs the old package's purge step. Because the instances have the same unit names, that step deletes their start-at-boot links. So purge it deliberately, then re-enable the instances:
```bash
# 1. If you customised it, copy the old settings file across (purging deletes it)
sudo cp /etc/default/terraria-instance /etc/default/terractl
# 2. Note which instances start at boot, purge the old package, re-enable them
enabled=$(ls /etc/systemd/system/multi-user.target.wants/ | grep '^terraria@')
sudo apt purge terraria-instance
for unit in $enabled; do sudo systemctl enable "$unit"; done
```
### Building from source
From the git repository:
```bash
make check # syntax-check the scripts (and shellcheck, if installed)
make deb # builds build/terraria-server-manager_<version>_all.deb
sudo make install # or install directly without a package (PREFIX=/usr/local)
sudo make uninstall # removes a 'make install' (keeps settings and data)
```
The version comes from the `VERSION` file. Set `Maintainer:` in `packaging/deb/control` to yourself before publishing packages.
### Changing locations
`/etc/default/terractl` can move the instances or zips directory. If you move the instances directory, the service also needs permission to write to the new location:
```bash
sudo systemctl edit terraria@.service
```
```ini
[Service]
ReadWritePaths=/your/new/path
```
---
## Migrating from the manual install
The earlier manual setup used `/opt/terraria`, `/terraria`, and a script called `terraria-instance` in `/usr/local/bin`. To move to the package:
```bash
# 1. Note which instances start at boot, then stop everything
ls /etc/systemd/system/multi-user.target.wants/ | grep terraria@
sudo systemctl stop 'terraria@*'
# 2. Remove the old files. These would override the package's versions:
# /etc/systemd/system takes precedence over /usr/lib/systemd/system, and
# /usr/local/bin comes before /usr/bin in PATH.
sudo rm /etc/systemd/system/terraria@.service /etc/systemd/system/terraria@.socket
sudo rm /usr/local/bin/terraria-instance
# 3. Install the package (keeps the existing 'terraria' user)
sudo apt install ./terraria-server-manager_2.0.0_all.deb
# 4. Move instances and zips
for d in /opt/terraria/*/; do
[ -f "$d/instance.conf" ] && sudo mv "$d" /var/lib/terraria/instances/
done
[ -d /opt/terraria/.deleted ] && sudo mv /opt/terraria/.deleted /var/lib/terraria/instances/
sudo mv /terraria/*.zip /var/lib/terraria/dist/
# 5. serverconfig.txt contains absolute paths; update them
sudo sed -i 's#/opt/terraria/#/var/lib/terraria/instances/#g' \
/var/lib/terraria/instances/*/serverconfig.txt
sudo chown -R terraria:terraria /var/lib/terraria/instances
# 6. Re-enable instances (the old boot links point at the deleted unit file)
sudo systemctl reenable terraria@calamity # repeat for each instance from step 1
sudo systemctl start terraria@calamity
```
Once everything works, remove the old directories: `sudo rm -rf /terraria /opt/terraria`. If you set `ZIP=` in an `instance.conf` to an absolute path under `/terraria`, change it to just the file name.
The existing `terraria` user keeps `/opt/terraria` as its home directory. That's harmless, because the script sets a per-instance home itself. To tidy it anyway, run `sudo usermod -d /var/lib/terraria terraria` after deleting `/opt/terraria`.
---
## Server zips
Put the official downloads in `/var/lib/terraria/dist`:
| Type | File name pattern | Where to get it |
|---|---|---|
| Vanilla | `terraria-server-*.zip` | terraria.org (the "PC Dedicated Server" download link) |
| tModLoader | `tModLoader*.zip` | tModLoader's GitHub releases page (`tModLoader.zip`) |
**Rename tModLoader zips to include the version**, for example `tModLoader-v2026.07.3.0.zip`. GitHub always calls the file `tModLoader.zip`, so renaming it lets you keep several versions side by side and pin instances to specific ones.
Zips are only read, never changed, and one zip can serve any number of instances.
**tModLoader zips don't include .NET.** Each tModLoader instance downloads the .NET runtime it needs into `server/dotnet/` on its first start, so that start needs internet access and `curl`. The download is kept when you upgrade or re-unpack the instance. tModLoader checks the .NET version on every start and fetches a different one itself if a new release needs it.
Members of the `terraria` group can copy zips into `dist/` without `sudo`:
```bash
scp tModLoader-v2026.07.3.0.zip you@yourserver:/var/lib/terraria/dist/
```
---
## Creating an instance
```bash
sudo terractl new <name> [vanilla|tmodloader] [port]
```
Examples:
```bash
sudo terractl new survival # vanilla, next free port
sudo terractl new calamity tmodloader # tModLoader, next free port
sudo terractl new pvp vanilla 7800 # vanilla on port 7800
```
If you don't give a port, the instance gets the next number after the highest port already used, starting at 7777.
This creates `/var/lib/terraria/instances/<name>/` with `instance.conf` and `serverconfig.txt`, but **does not start the server**. Before the first start, review `serverconfig.txt` (world size, difficulty, password). For a tModLoader instance, also add your mods first (see [Mods](#mods-tmodloader)): the world is generated on first start, and it should be generated with the mods loaded so their world content is created.
Instance names may contain letters, digits, `_`, `-` and `.`, and must not start with `.`.
### Quick start: a new modded server
```bash
sudo terractl new calamity tmodloader
sudo terractl mods calamity add ~/tmod-export # your mods, before the first start
sudo nano /var/lib/terraria/instances/calamity/serverconfig.txt # optional: world size, difficulty
sudo systemctl enable --now terraria@calamity
sudo terractl attach calamity # watch it load and generate the world
```
You can also skip `new` and just run `sudo systemctl start terraria@<name>`. That creates a **vanilla** instance with default settings; to switch it to tModLoader, change `TYPE` in its `instance.conf` and restart.
---
## Starting, stopping and boot
Instances are ordinary systemd services:
```bash
sudo systemctl start terraria@calamity
sudo systemctl stop terraria@calamity # saves the world, then exits
sudo systemctl restart terraria@calamity
sudo systemctl enable terraria@calamity # start at boot
sudo systemctl disable terraria@calamity # don't start at boot
sudo systemctl enable --now terraria@calamity # both at once
systemctl status terraria@calamity
```
To see all instances with their type, port and state:
```bash
sudo terractl list
```
Behaviour to be aware of:
- **Crashes restart automatically** after 10 seconds.
- **A clean exit does not restart.** If you type `exit` in the server console, the server saves and stops, and stays stopped until you start it again.
- **The first start of a tModLoader instance is slow.** It downloads .NET, loads the mods and generates the world, which can take several minutes.
- **Stopping waits for the save.** The server gets up to 80 seconds to save and exit before systemd forces it to stop.
---
## The server console
Each server reads console commands (`save`, `say`, `kick`, `playing`, and so on) from its pipe, and its output goes to the systemd journal.
### Interactive: `attach`
```bash
sudo terractl attach calamity
```
This shows the last 30 lines of output and then follows it live. Anything you type is sent to the server, with arrow-key editing and history. **Ctrl-D** or **Ctrl-C** detaches, and the server keeps running.
`attach` will not send `exit`, since that would stop the server. Use `systemctl stop` to stop it.
The one visible difference from a real terminal: the server's `:` prompt only appears once a newline follows it.
### One-off commands: `cmd`
```bash
sudo terractl cmd calamity save
sudo terractl cmd calamity say Restarting in 5 minutes
```
This is useful in scripts and cron jobs. The command's output appears in the journal.
### Logs only
```bash
journalctl -u terraria@calamity -f # follow
journalctl -u terraria@calamity -n 100 # last 100 lines
journalctl -u terraria@calamity --since today
```
tModLoader also writes its own logs to `/var/lib/terraria/instances/<name>/server/tModLoader-Logs/`:
- `Launch.log` covers the .NET download and startup.
- `server.log` covers mod loading and gameplay.
---
## Configuration
Each instance has two config files for two different purposes.
### `serverconfig.txt`: Terraria's settings
This is Terraria's standard server config, read by the game itself. The generated default:
```ini
world=/var/lib/terraria/instances/<name>/data/Worlds/<name>.wld
worldpath=/var/lib/terraria/instances/<name>/data/Worlds
autocreate=2 # create world if missing: 1=small 2=medium 3=large
worldname=<name>
difficulty=0 # 0=classic 1=expert 2=master 3=journey
#seed=
port=7777
maxplayers=8
password=
motd=Welcome to <name>
secure=1
upnp=0
language=en-US
banlist=/var/lib/terraria/instances/<name>/data/banlist.txt
```
`autocreate`, `worldname`, `difficulty` and `seed` only matter when the world file doesn't exist yet. Once the world has been created, changing them has no effect.
Changes take effect on restart. Every instance must use a different `port`.
To use an existing world, copy the `.wld` file (plus `.twld` for tModLoader worlds) into `data/Worlds/` and point `world=` at it.
### `instance.conf`: how the instance runs
The game never reads this file. The helper script reads it at every start.
```bash
TYPE=tmodloader # vanilla | tmodloader
ZIP= # zip to use; blank = newest matching zip at first install
EXTRA_ARGS=() # extra server arguments, as a bash array
```
| Setting | Effect |
|---|---|
| `TYPE` | Which server software runs. Changing it re-unpacks `server/` from the other kind of zip on the next start. `data/` and `serverconfig.txt` are kept. |
| `ZIP` | Pins a specific zip from `/var/lib/terraria/dist` (a file name, or an absolute path). Changing it re-unpacks `server/` on the next start. If left blank, the newest matching zip is used at first install and **never upgraded automatically**. |
| `EXTRA_ARGS` | Extra command-line arguments, e.g. `EXTRA_ARGS=(-disableannouncementbox)`. |
This file also marks the directory as an instance: `list` ignores directories without one, and `delete` refuses to remove them.
---
## Mods (tModLoader)
Mods only work on tModLoader instances. Each instance has its own separate set of mods:
```
/var/lib/terraria/instances/<name>/data/Mods/
├── enabled.json ["CalamityMod", "MagicStorage"]
├── CalamityMod.tmod
└── MagicStorage.tmod
```
`enabled.json` lists which mods to load, using each file's name without `.tmod`. Every mod's dependencies must also be present and listed. Mod settings live in `data/ModConfigs/`.
You don't need to edit these by hand: `terractl mods` manages them and sets the file ownership correctly (see [Managing mods](#managing-mods)). The `Mods` folder is created automatically for tModLoader instances.
Players don't need to install mods themselves: tModLoader offers to download the server's mods when they join.
### Exporting mods from Windows
tModLoader on Windows keeps its files in these places:
| What | Where |
|---|---|
| Which mods are enabled | `Documents\My Games\Terraria\tModLoader\Mods\enabled.json` |
| Workshop mods | `<Steam library>\steamapps\workshop\content\1281930\<id>\` |
| Manually installed mods | `Documents\My Games\Terraria\tModLoader\Mods\` |
| Mod settings | `Documents\My Games\Terraria\tModLoader\ModConfigs\` |
Workshop mods often contain **several builds**, in folders named after the tModLoader version they target (`2024.3`, `2025.3`, ...). The game loads the build from the highest folder that isn't newer than the running tModLoader.
`export-tmod-mods.ps1` applies the same rule. It's installed on the server at `/usr/share/terraria-server-manager/`; copy it to your PC first. It collects every enabled mod, a matching `enabled.json` and your mod settings into `Desktop\tmod-export`, laid out like the server's `data/` folder. Run it in PowerShell:
```powershell
powershell -ExecutionPolicy Bypass -File .\export-tmod-mods.ps1 -MaxVersion 2026.7
```
Set `-MaxVersion` to your tModLoader version as year.month. The full version is shown at the bottom-left of tModLoader's main menu, e.g. `v2026.07.3.0` → `2026.7`. For the preview branch, add `-TmlDir "$HOME\Documents\My Games\Terraria\tModLoader-preview"`.
The script prints which file it chose for each mod, and warns about any it couldn't find. It leaves missing mods out of `enabled.json`, because one missing file would stop the server loading any mods.
### Copying mods to the server
Copy the export folder to the server. `scp` is built into Windows 10 and 11:
```powershell
scp -r "$HOME\Desktop\tmod-export" you@yourserver:
```
This puts it in your home directory on the server. Then install the mods into the instance:
```bash
sudo terractl mods calamity add ~/tmod-export
sudo systemctl restart terraria@calamity # only if it's already running
```
`add` copies the `.tmod` files and any `ModConfigs`, gives them to the `terraria` user, and adds them to `enabled.json`. For a new instance, do this **before the first start** so the world is generated with the mods loaded.
### Managing mods
```bash
terractl mods <name> # list installed mods and whether each is enabled
terractl mods <name> add <folder|file.tmod>... # install and enable
terractl mods <name> disable <Mod>... # keep installed, but don't load
terractl mods <name> enable <Mod>... # load again
terractl mods <name> remove <Mod>... # uninstall
```
`add` accepts `.tmod` files, a folder containing them, or an export folder with `Mods/` and `ModConfigs/` subfolders. Mod names are the file names without `.tmod`, e.g. `CalamityModMusic`.
Adding a mod that's already installed replaces it, which is how you update a mod. Changes take effect when the server restarts; the command reminds you if it's running.
The list also flags mods that are **enabled but missing**, because the server won't load any mods while one is missing.
`add` doesn't check dependencies, so remember to add them too. If one is missing, `server/tModLoader-Logs/server.log` says which.
---
## Versions and upgrades
### Players must match the server's version
- **Vanilla:** players need the same Terraria version as the server's zip. For example, `terraria-server-1458.zip` is Terraria 1.4.5.8.
- **tModLoader:** players need exactly the same tModLoader version, including the same branch (stable or preview). Steam updates players' tModLoader automatically, usually monthly, so a server that was matching can become mismatched after an update.
Stable tModLoader and the latest vanilla Terraria can be based on different Terraria versions. That's fine, because every instance runs independently.
### Upgrading (or downgrading) an instance
1. Put the new zip in `/var/lib/terraria/dist`, with the version in its name.
2. Set `ZIP=` to that file name in the instance's `instance.conf`.
3. Restart the instance:
```bash
sudo systemctl restart terraria@calamity
```
The helper sees that the zip has changed and re-unpacks `server/`. Nothing in `data/` is touched. To move back, set `ZIP=` to the old file.
**Pin `ZIP=` on modded instances.** Pinned instances never change version by accident, and you can see at a glance which version each one is on. New tModLoader versions can break older mods, so only upgrade a modded server once its mods support the new version.
### Re-unpacking the same version
If `server/` gets damaged or you want a clean copy:
```bash
sudo terractl reinstall calamity
sudo systemctl restart terraria@calamity
```
For tModLoader, the downloaded .NET runtime and `tModLoader-Logs/` are kept. If you suspect .NET itself is damaged, use `--clean` to delete them too, so .NET is downloaded again on the next start:
```bash
sudo terractl reinstall calamity --clean
sudo systemctl restart terraria@calamity
```
---
## Deleting and restoring
```bash
sudo terractl delete calamity # asks you to type the name
sudo terractl delete calamity -y # no prompt
sudo terractl delete calamity -y --no-backup # permanently, no backup
```
This stops and disables the service, then removes the whole instance directory. Unless you pass `--no-backup`, it first archives the configs and `data/` to `/var/lib/terraria/instances/.deleted/<name>-<timestamp>.tar.gz`. `server/` is left out of the archive because it can be recreated from the zip.
To restore an instance:
```bash
sudo tar -xzf /var/lib/terraria/instances/.deleted/calamity-20260928-134422.tar.gz -C /var/lib/terraria/instances
sudo chown -R terraria:terraria /var/lib/terraria/instances/calamity
sudo systemctl enable --now terraria@calamity # server/ is unpacked again on start
```
`.deleted/` is never cleaned up automatically. Remove old archives yourself.
---
## Backups
Everything worth keeping is in `data/`, `instance.conf` and `serverconfig.txt`.
To back up a running server, save it first so the world file on disk is current:
```bash
sudo terractl cmd calamity save
sleep 10
sudo tar -czf /root/calamity-$(date +%F).tar.gz -C /var/lib/terraria/instances \
--exclude=calamity/server calamity
```
Stopping the server first is the safest option. Terraria also keeps a `.bak` copy of each world alongside the world file.
---
## Permissions
The servers always run as the `terraria` user. Administration is simplest with `sudo`.
| Command | Who can run it | Notes |
|---|---|---|
| `new` | root (or `terraria`) | Run as root, it sets ownership of the new instance to `terraria` itself. |
| `list` | root, or members of the `terraria` group | `/var/lib/terraria` is not readable by other users. |
| Adding zips to `dist/` | root, or members of the `terraria` group | |
| `attach` | root, or members of **both** `terraria` and `systemd-journal` | Needs to write to the console pipe and read the journal. |
| `cmd` | root, or members of the `terraria` group | Writes to the console pipe. |
| `reinstall` | root (or `terraria`) | |
| `mods <name>` (list) | root, or members of the `terraria` group | |
| `mods add/enable/disable/remove` | root (or `terraria`) | Run as root, it sets ownership of the files it installs to `terraria`. |
| `delete` | root only | Checked by the script. |
| `prepare`, `run` | Normally only the service | Run as root, they automatically switch to the `terraria` user, so no files end up owned by root. Any other user is refused. |
| `stop` | Only the service | Needs the server's process ID from systemd. |
| `systemctl start/stop/enable` | root | |
| `journalctl -u terraria@...` | root, or members of `systemd-journal` or `adm` | |
To run the everyday commands without `sudo`:
```bash
sudo usermod -aG terraria,systemd-journal youruser # then log out and back in
```
Members of the `terraria` group can read server files (useful for backups) but cannot change them. Files the service creates are readable, not writable, by the group.
### Avoiding root-owned files
The helper commands don't leave root-owned files behind, and `mods add` handles mods and ModConfigs. The remaining risk is **copying or creating other files in an instance directory as root**, such as worlds. The server can't write to root-owned files, so saving fails or tModLoader errors. After copying anything in, run:
```bash
sudo chown -R terraria:terraria /var/lib/terraria/instances/<name>
```
Alternatively, copy files as the `terraria` user to begin with:
```bash
sudo -u terraria cp MyWorld.wld /var/lib/terraria/instances/calamity/data/Worlds/
```
Editing an existing file with `sudo nano` keeps its owner, so it's fine.
---
## Running by hand for debugging
Stop the service first, so two copies of the same server don't run at once on the same port and world:
```bash
sudo systemctl stop terraria@calamity
sudo terractl run calamity # automatically runs as 'terraria'
```
The server runs in your terminal and you can type commands into it directly. Type `exit` to stop it, then start the service again:
```bash
sudo systemctl start terraria@calamity
```
---
## Troubleshooting
**tModLoader stops with `Aborted` right after "Launched Using Local Dotnet".**
The ICU library is missing, so .NET aborts at startup. Fix it with `sudo apt install libicu-dev`, then restart the instance. To confirm, check that `ldconfig -p | grep -c libicu` prints more than 0.
**`ls: cannot access '.../dotnet/shared/Microsoft.NETCore.App'` on the first start.**
Harmless. tModLoader looks for old .NET versions to clean up before any have been installed.
**The first tModLoader start fails while downloading .NET.**
The server needs internet access and `curl` on its first start. See `server/tModLoader-Logs/Launch.log` for the exact error.
If the host has no internet access, you can copy the `dotnet` folder from another tModLoader instance that runs the same tModLoader version. Unpack the new instance first, then copy the folder in before starting it:
```bash
sudo terractl prepare <name> # unpacks server/ without starting
sudo cp -a /var/lib/terraria/instances/other/server/dotnet /var/lib/terraria/instances/<name>/server/
sudo systemctl start terraria@<name>
```
**The server can't save, or you see "Permission denied" errors.**
Some files are owned by root. Run `sudo chown -R terraria:terraria /var/lib/terraria/instances/<name>`.
**Players can't join a tModLoader server with a version-mismatch message.**
The player's tModLoader version differs from the server's. See [Versions and upgrades](#versions-and-upgrades).
**Mods don't load.**
Run `terractl mods <name>` to check for mods that are enabled but missing. Then check `server/tModLoader-Logs/server.log` for the cause. Common ones:
- a dependency is missing from `data/Mods/`;
- a mod name in `enabled.json` doesn't match its `.tmod` file name;
- a mod build was made for a newer tModLoader than the server (re-export with `-MaxVersion`);
- the files are owned by root.
**The server fails with "address already in use".**
Two instances share a port, or a copy you started by hand is still running. Check with `sudo terractl list` and `pgrep -af 'tModLoader.dll|TerrariaServer'`.
**The service starts but you can't connect.**
Check the port in `serverconfig.txt`, your firewall (TCP), and router port forwarding.
**The server isn't restarting after it stopped.**
It exited cleanly, probably via `exit` in the console, so systemd leaves it stopped. Start it again with `systemctl start`.
**The world was created without mod content.**
It was generated before the mods were added. Delete the world files in `data/Worlds/` (or point `world=` at a new name) and restart to generate a new one.
---
## Command reference
```
terractl new <name> [vanilla|tmodloader] [port] Create an instance (not started)
terractl list Show instances, type, port, state
terractl attach <name> Interactive console (Ctrl-D detaches)
terractl cmd <name> <command...> Send one console command
terractl reinstall <name> [--clean] Re-unpack server/ on next start
(--clean also re-downloads .NET)
terractl mods <name> [list] Show installed mods
terractl mods <name> add <folder|file.tmod>... Install and enable mods (and ModConfigs)
terractl mods <name> enable|disable <Mod>... Change which mods load
terractl mods <name> remove <Mod>... Uninstall mods
terractl delete <name> [-y] [--no-backup] Stop, disable, back up and remove
terractl --version Show the installed version
Used by the service:
terractl prepare <name> Create dirs/configs, unpack zip if needed
terractl run <name> Run the server
terractl stop <name> <pid> Save and shut down via the console
```
```
systemctl start|stop|restart|status terraria@<name>
systemctl enable|disable [--now] terraria@<name>
journalctl -u terraria@<name> [-f] [-n N]
```