Portable Linux toolkit for running and managing a tModLoader dedicated server.
This repo is meant to be its own server home. By default the engine, worlds, mods, configs, logs, backups, and optional SteamCMD install all live under the project root. make setup turns the tracked example files into local working files, so the project can be cloned, moved, backed up, or published without dragging machine-specific paths through git.
- Repo-local layout with
Engine/,Mods/,Worlds/,ModConfigs/,Backups/,Logs/, and optionalTools/SteamCMD/ - Persistent Go control room with live server snapshot, log tails, in-app command output, and mouse-friendly navigation
- Native control-room flows for Workshop URL or ID queueing, installed-mod load management, and per-mod config editing
- Manifest-driven addon sections from
Addons/*/addon.json, so new tool groups can plug into the UI without editing core code - Shell hub kept for automation and direct command entrypoints while interactive launches go straight to the Go control room
- Backup, monitoring, diagnostics, and workshop maintenance still live in the same repo-local toolkit
- Local-only files and Go build artifacts are already covered by
.gitignore
Debian / Ubuntu:
sudo apt update -y # refresh package metadata
sudo apt install -y git screen curl jq pigz rsync unzip net-tools dos2unix htop ncdu # core runtime and admin tools
sudo apt install -y golang # Go toolchain for make tui-run and shell-launched UIFedora:
sudo dnf install -y git screen curl jq pigz rsync unzip net-tools dos2unix htop ncdu # core runtime and admin tools
sudo dnf install -y golang # Go toolchain for make tui-run and shell-launched UINotes:
golangis required formake tui-runor forbash Scripts/hub/tmod-control.shwhen no built binary exists yet.screenis required for normal server start and stop flows.
make setup # create repo-local directories and missing local config filesThis creates the expected directory layout, copies local working files from tracked examples, and makes the scripts executable. Existing local files are left alone.
Created on first run:
Configs/serverconfig.txtfromConfigs/serverconfig.example.txtScripts/env.shfromScripts/env.example.shScripts/steam/mod_ids.txtfromScripts/steam/mod_ids.example.txt
make steamcmd-local # install SteamCMD into Tools/SteamCMD/This installs SteamCMD into Tools/SteamCMD/steamcmd.sh, which matches the default steamcmd_path in Configs/serverconfig.txt.
Run this if you want Workshop downloads, or if you prefer installing the engine through SteamCMD instead of GitHub releases.
Recommended public-friendly path:
make engine-github # download and extract the latest tModLoader release into Engine/This downloads the latest official tModLoader.zip release from GitHub and extracts it into Engine/.
Alternative SteamCMD path:
export STEAM_USERNAME="your_steam_username" # Steam account used for owned-app installs
./Tools/SteamCMD/steamcmd.sh \ # run the local SteamCMD install
+force_install_dir "$PWD/Engine" \ # place server files in this repo's Engine/
+login "$STEAM_USERNAME" \ # authenticate with your Steam account
+app_update 1281930 validate \ # install or update tModLoader server files
+quit # exit SteamCMDNotes:
1281930is the tModLoader app ID.- Steam reports that app as requiring Terraria ownership (
105600), so anonymous SteamCMD downloads can appear to succeed while leavingEngine/empty. - The GitHub release path avoids that first-run trap for public users.
After a successful install, Engine/ should contain tModLoader.dll, tModLoader.runtimeconfig.json, and start-tModLoaderServer.sh.
Run the Go UI from source:
make tui-run # launch the Go control room from sourceRun with verbose terminal logging:
TMOD_DEBUG=1 make tui-run # launch the Go control room with verbose terminal loggingUse the shell entrypoint to open the Go control room from the repo root:
bash Scripts/hub/tmod-control.sh tui # launch the Go control room through the shell hubIf Go is not installed on the host yet, build the binary first and then use the same shell entrypoint:
make tui-build # build bin/tmodloader-ui once
bash Scripts/hub/tmod-control.sh tui # launch the built control roomOptional extras:
make tui-build # build bin/tmodloader-ui
make install-man # install the man page system-wide
make help # list the available make targets<project-root>/
├── Engine/ # tModLoader server files
├── Mods/ # Installed .tmod files + enabled.json
├── Worlds/ # World save files
├── ModConfigs/ # Per-mod config files
├── Configs/
│ ├── serverconfig.example.txt
│ ├── serverconfig.txt # local, created by make setup
│ └── workshop_map.json # optional local Workshop-ID hints
├── Backups/
│ ├── Worlds/
│ ├── Configs/
│ └── Full/
├── Logs/ # Script, server, monitor, backup, and dotnet logs
├── Tools/
│ └── SteamCMD/
├── Addons/ # optional manifest-driven UI extensions
├── Scripts/
│ ├── env.example.sh
│ ├── env.sh # local, created by make setup
│ ├── backup/
│ ├── core/
│ ├── diag/
│ ├── hub/
│ └── steam/
│ ├── mod_ids.example.txt
│ └── mod_ids.txt # local, created by make setup
└── Testing/
├── local/ # ignored scratch scripts
├── output/ # ignored captured output
└── tmp/ # ignored disposable workspace
Tracked examples stay in git. Generated working copies, runtime content, and scratch space stay local.
bash Scripts/hub/tmod-control.sh tui # launch the Go control room
bash Scripts/hub/tmod-control.sh interactive # accepted alias for the same control roomGo UI basics:
Enteropens a section or runs the selected actionrrefreshes status and the current log viewlcycles between log files when log-tail view is activeTabswitches between log-tail view and command-output viewEscreturns to the section overviewqquits when idle- mouse hover and click can select and activate rows when your terminal forwards mouse events
The left legend stays visible on every screen and gives the currently valid hotkeys a little more contrast, so the control scheme stays learn-once instead of page-by-page.
Workshop tools in the Go UI now include native screens for Add Mod by URL or ID, Manage Installed Mods, and Edit Mod Configs. The config editor still hands off to your terminal editor and returns you to the TUI when you exit.
Addon action packs under Addons/*/addon.json are loaded into the control room automatically. If an addon manifest is invalid, the control room shows a warning in the header and command-output pane, and the loader details are still written to Logs/control.log.
bash Scripts/hub/tmod-control.sh start # start the server in its managed screen session
bash Scripts/hub/tmod-control.sh stop # stop the running server cleanly
bash Scripts/hub/tmod-control.sh restart # restart the managed server process
bash Scripts/hub/tmod-control.sh status # print a quick status summarybash Scripts/hub/tmod-control.sh workshop download # download Workshop mods listed in mod_ids.txt
bash Scripts/hub/tmod-control.sh workshop sync # copy compatible Workshop mods into Mods/
bash Scripts/hub/tmod-control.sh workshop sync --yes # run sync non-interactively
bash Scripts/hub/tmod-control.sh workshop list # inspect downloaded Workshop mods
bash Scripts/hub/tmod-control.sh workshop archive # archive old incompatible mod versions
bash Scripts/hub/tmod-control.sh workshop archive --yes # run archival non-interactively
bash Scripts/hub/tmod-control.sh workshop cleanup # remove incomplete Workshop leftovers
bash Scripts/hub/tmod-control.sh workshop status # show Workshop paths and SteamCMD statusbash Scripts/hub/tmod-control.sh backup worlds # back up world save files
bash Scripts/hub/tmod-control.sh backup configs # back up server and script config files
bash Scripts/hub/tmod-control.sh backup full # create a full repo-local server backup
bash Scripts/hub/tmod-control.sh backup auto # run the default automatic backup flow
bash Scripts/hub/tmod-control.sh monitor start # start the background health monitor
bash Scripts/hub/tmod-control.sh monitor status # inspect monitor status
bash Scripts/hub/tmod-control.sh diagnostics # run the full diagnostics entrypoint
bash Scripts/diag/tmod-diagnostics.sh report # generate a standalone diagnostics reportFor the full command surface:
bash Scripts/hub/tmod-control.sh help # print the shell hub usage summary
man tmod-control # open the installed man pagemake setup creates this file from Configs/serverconfig.example.txt. It is the main local server config and is intentionally gitignored.
Useful notes:
world=andworldname=are usually managed by the world pickersteamcmd_pathdefaults to./Tools/SteamCMD/steamcmd.sh- script settings live under the
tmod-scriptssection at the bottom - paths support absolute values,
~/..., and project-relative paths like./Tools/SteamCMD/steamcmd.sh
Example:
# ─── tmod-scripts ─────────────────────────────────────────────────────────────
steamcmd_path=./Tools/SteamCMD/steamcmd.sh
log_max_size=10M
log_keep_days=14make setup creates this file from Scripts/env.example.sh. Use it for local values you do not want in tracked files, such as:
STEAM_USERNAMESTEAM_API_KEY- webhook URLs
- machine-specific overrides
For Workshop downloads, STEAM_USERNAME is optional. Anonymous SteamCMD access is used as a fallback, but a real Steam account may be more reliable for larger download batches.
make setup creates this file from Scripts/steam/mod_ids.example.txt if it is missing. It is local and gitignored. Add one Steam Workshop URL or numeric ID per line. Lines starting with # are ignored.
The Go UI can manage this file natively through Workshop / Add Mod by URL or ID, and Workshop / Manage Installed Mods writes the matching Mods/enabled.json load list:
bash Scripts/hub/tmod-control.sh mods add https://steamcommunity.com/sharedfiles/filedetails/?id=2824688804 # add by Workshop URL
bash Scripts/hub/tmod-control.sh mods add 2824688804 # add by numeric Workshop IDDirect editing works fine too.
Optional local file for mapping mod names to Workshop IDs when dependency helpers need a manual hint.
{
"MyMod": "1234567890"
}You do not need to install the tModLoader runtime manually. On first server start, the toolkit reads the required version from Engine/tModLoader.runtimeconfig.json and runs tModLoader's bundled installer into Engine/dotnet/.
If that install fails, check:
Logs/dotnet-install.log- that
Engine/contains a valid tModLoader server install - that the host can reach the required download endpoints
.gitignore already excludes:
Engine/,Mods/,Worlds/,ModConfigs/,Backups/,Logs/, andTools/SteamCMD/Configs/serverconfig.txtandConfigs/workshop_map.jsonScripts/env.shandScripts/steam/mod_ids.txtTesting/local/,Testing/output/, andTesting/tmp/bin/,tmodloader-ui,cmd/tmodloader-ui/tmodloader-ui,coverage.out,*.coverprofile,*.test,*.prof, and*.pprof
That keeps the public repo clean while still letting the project behave like a complete local server workspace.
The Go control room can load extra sections and actions from addon manifests in Addons/<addon-name>/addon.json.
Each manifest defines a default section plus one or more actions. By default, addon actions run with their own addon directory as the working directory, so local helper scripts can be referenced directly.
Example structure:
Addons/
└── admin-tools/
├── addon.json
└── scripts/
├── audit-world.sh
└── rotate-admin-tokens.sh
Example manifest:
{
"name": "admin-tools",
"section": "Admin",
"actions": [
{
"title": "Audit World",
"description": "Run the world audit helper.",
"command": ["bash", "scripts/audit-world.sh"]
},
{
"title": "Rotate Admin Tokens",
"description": "Rotate admin auth material.",
"command": ["bash", "scripts/rotate-admin-tokens.sh"],
"confirm_text": "Rotate admin tokens now?"
}
]
}Supported manifest fields:
name: optional label for the addon bundlesection: default UI section name for all actions in the fileactions[].section: optional per-action override if one addon needs multiple sectionsactions[].title: action label shown in the UIactions[].description: short help text for the selected action panelactions[].command: argv array to executeactions[].confirm_text: optional confirm prompt before runningactions[].working_dir: optional working directory
actions[].command and actions[].working_dir also support ${repo_dir} and ${addon_dir} placeholders. Invalid addon manifests are skipped with warnings in the control room and details in Logs/control.log.
See Addons/README.md for the same rules in a smaller reference format.
Release history lives in CHANGELOG.md.
Add new user-facing changes to Unreleased there so the README can stay focused on setup, usage, and the self-contained layout.
No GitHub release is published yet, but the current documented state of the public portable edition is v2.6.0.
Issues, feature requests, and pull requests are welcome.
See CONTRIBUTING.md for contribution guidelines and SECURITY.md for responsible disclosure.
Apache 2.0. See LICENSE for details.