Portable Linux toolkit for running and managing a tModLoader dedicated server.
This edition is built around a self-contained project layout: the server engine, worlds, mods, logs, backups, and optional local SteamCMD install all live inside the repo folder by default. That makes it easier to clone, move, test, back up, and publish without dragging around machine-specific paths.
- Portable by default: the project folder acts as the server home.
- Public-repo friendly: runtime data and local machine config stay out of git.
- Practical for real hosting: workshop sync, backups, monitoring, diagnostics, and world management are already wired together.
- Easy to bootstrap:
make setupprepares the layout,make steamcmd-localinstalls SteamCMD locally, andmake engine-githubinstalls the engine from the official GitHub release.
- Persistent Go TUI with section overview, live server snapshot, log tail, and in-app command output
- Shell hub preserved as a CLI backend and legacy fallback for users who still want menu-driven Bash
- Live host and process metrics for PID, players, mods, backups, CPU, memory, uptime, disk activity, and temperature
- Repo-local layout with
Engine/,Mods/,Worlds/,Logs/,Backups/, andTools/SteamCMD/ - Engine bootstrap via official GitHub release or SteamCMD
- Steam Workshop tooling for mod download, sync, archive, and cleanup
- Built-in backup flows for worlds, configs, and full-server snapshots
- Diagnostics and repair helpers for common setup mistakes
- Per-mod config editing from the control menu
- Log rotation and project-relative config path support
- Install system packages.
- Run
make setup. - Optionally run
make steamcmd-local. - Install tModLoader server files into
Engine/. - Start the persistent control room.
Debian / Ubuntu:
sudo apt update -y
sudo apt install -y git screen curl jq pigz rsync unzip htop ncdu net-tools dos2unix fzf dialog golangFedora:
sudo dnf install -y git screen curl jq pigz rsync unzip htop ncdu net-tools dos2unix fzf dialog golangmake setupThis creates the expected directory layout, copies local config templates, and makes the scripts executable.
make steamcmd-localThis installs SteamCMD into Tools/SteamCMD/steamcmd.sh, which matches the default steamcmd_path in Configs/serverconfig.txt.
If you plan to use Workshop downloads, install SteamCMD even if you used make engine-github for the engine itself.
Recommended public-friendly path:
make engine-githubThis downloads the latest official tModLoader.zip release from GitHub and extracts it into Engine/.
Alternative SteamCMD path:
export STEAM_USERNAME="your_steam_username"
./Tools/SteamCMD/steamcmd.sh \
+force_install_dir "$PWD/Engine" \
+login "$STEAM_USERNAME" \
+app_update 1281930 validate \
+quitNotes:
1281930is the tModLoader app ID.- Steam reports that app as requiring ownership of Terraria (
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 the tModLoader binaries, tModLoader.dll, tModLoader.runtimeconfig.json, and start-tModLoaderServer.sh.
make tui-runFor verbose terminal logging:
TMOD_DEBUG=1 make tui-runShell entrypoint that now prefers the Go control room:
bash Scripts/hub/tmod-control.sh<project-root>/
├── Engine/ # tModLoader server files
├── Mods/ # Installed .tmod files + enabled.json
├── Worlds/ # World save files
├── ModConfigs/ # Per-mod config files
├── Configs/ # Server and workshop config
├── Backups/
│ ├── Worlds/
│ ├── Configs/
│ └── Full/
├── Logs/ # Script, server, monitor, and dotnet logs
├── Tools/
│ └── SteamCMD/
└── Scripts/
├── backup/
├── core/
├── diag/
├── hub/
└── steam/
Everything above is expected to live inside the project by default. That is the main difference between this public portable repo and the older machine-specific setup it came from.
make tui-runThis is the new primary interface. It keeps the screen alive while commands run, streams backend script output inside the app, refreshes server status in place, and lets you cycle through the project log files without dropping back to raw shell output.
The default landing view is a section overview, so you drill into Server, Workshop, Backup, Monitor, Diagnostics, or Maintenance instead of starting on one long action list. The right side of the UI stays anchored around a selected-section/action panel, a live Server Snapshot, and a lower pane for log tails or command output.
The shell entrypoint now prefers the same control room when it can find a built bin/tmodloader-ui or a local Go toolchain:
bash Scripts/hub/tmod-control.shUseful keys:
Enter: open the selected section or run the selected actionr: refresh status and the current log viewl: cycle betweenserver.log,control.log,workshop.log,backup.log,monitor.log, anddiagnostics.logTab: switch between log-tail view and command-output viewShift+Left/Shift+Right: horizontal scroll in the lower pane when output is wider than the windowEsc: return to the section overview from a category page- Mouse wheel: move one item at a time in the current list
q: quit when idleCtrl+C: force quit immediately
bash Scripts/hub/tmod-control.sh interactive classicIf you want the older searchable shell palette instead of the Go TUI, force the legacy path:
TMOD_FORCE_LEGACY_UI=1 bash Scripts/hub/tmod-control.sh interactiveWhen available, the legacy hub uses fzf for searchable pickers and dialog for boxed menus and log viewers. You can force a legacy shell mode with TMOD_UI_MODE=dialog, TMOD_UI_MODE=fzf, or TMOD_UI_MODE=plain.
Main areas exposed through the palette:
Server: start, stop, restart, select world, create world, import worldMods: add by Workshop URL or ID, toggle enabled mods, inspect downloads, edit mod configsMonitoring: dashboard, health check, live monitor, log viewing, console attachBackup: create, restore, verify, and clean up backupsMaintenance: diagnostics, engine update, emergency controls
bash Scripts/hub/tmod-control.sh start
bash Scripts/hub/tmod-control.sh stop
bash Scripts/hub/tmod-control.sh restart
bash Scripts/hub/tmod-control.sh statusbash Scripts/hub/tmod-control.sh workshop download
bash Scripts/hub/tmod-control.sh workshop sync
bash Scripts/hub/tmod-control.sh workshop sync --yes
bash Scripts/hub/tmod-control.sh workshop list
bash Scripts/hub/tmod-control.sh workshop archive
bash Scripts/hub/tmod-control.sh workshop archive --yes
bash Scripts/hub/tmod-control.sh workshop cleanupbash Scripts/hub/tmod-control.sh backup worlds
bash Scripts/hub/tmod-control.sh backup configs
bash Scripts/hub/tmod-control.sh backup full
bash Scripts/hub/tmod-control.sh backup auto
bash Scripts/backup/tmod-backup.sh restore --yes Backups/Worlds/worlds_YYYYMMDD_HHMMSS.tar.gzbash Scripts/diag/tmod-diagnostics.sh quick
bash Scripts/diag/tmod-diagnostics.sh full
bash Scripts/diag/tmod-diagnostics.sh binaries
bash Scripts/diag/tmod-diagnostics.sh config
bash Scripts/diag/tmod-diagnostics.sh fix
bash Scripts/diag/tmod-diagnostics.sh reportmake setup creates Configs/serverconfig.txt from the tracked example file. This local config 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 script settings:
# ─── tmod-scripts ─────────────────────────────────────────────────────────────
steamcmd_path=./Tools/SteamCMD/steamcmd.sh
log_max_size=10M
log_keep_days=14make setup also creates Scripts/env.sh 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. The toolkit will fall back to anonymous SteamCMD access if it is unset, but a real Steam account may be more reliable for larger download batches.
This file is also local and gitignored. It accepts one Steam Workshop URL or numeric ID per line. Lines starting with # are ignored.
The control hub can manage it for you through the Mods page, but direct editing works fine too.
Optional file for mapping mod names to Workshop IDs when dependency helpers need a manual hint.
Example:
{
"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
The repo is set up so that runtime data stays local. .gitignore already excludes:
Engine/,Mods/,Worlds/,Backups/,Logs/,Tools/SteamCMD/Configs/serverconfig.txtScripts/env.shScripts/steam/mod_ids.txtbin/,coverage.out,*.coverprofile, and*.testTesting/local/,Testing/output/, andTesting/tmp/
That keeps the public repo clean while still letting the project behave like a complete local server workspace.
Persistent Go TUI
- Added a Bubble Tea-based headless server console that keeps the screen alive while backend actions run.
- Replaced the old catch-all landing list with a section overview, native section pages, and a broader set of shell-backed admin actions.
- Added
make tui-runandmake tui-buildso the Go UI is easy to launch from source or build intobin/tmodloader-ui. - Made
bash Scripts/hub/tmod-control.shprefer the Go TUI for interactive launches, while keepinginteractive classicandTMOD_FORCE_LEGACY_UI=1for the legacy shell UI.
Observability & Layout
- Added a live
Server Snapshotwith running state, PID, world, players, mod and backup counts, CPU, RSS memory, uptime, disk activity, and host temperature. - Kept log tails and command output inside the app so server actions no longer dump you back into raw shell output.
- Tightened pane layout, empty states, mouse-wheel handling, and overview/action previews so the interface behaves more like a persistent SSH console than a shell launcher.
- Hardened status polling so offline states do not flicker or report bogus PID, memory, or uptime values.
Compatibility
- The Bash control hub remains available as a legacy fallback and backend command surface.
- Existing script-driven workflows continue to work through the same
tmod-control.sh, backup, workshop, diagnostics, and monitor commands.
Headless UI
- Added a dependency-aware interactive hub that can use
dialogfor boxed menus/log viewers andfzffor searchable pickers, while keeping plain-Bash fallback intact. - Expanded the command palette into a fuller direct-action launcher instead of mostly routing through submenu pages.
- Unified page navigation around shared menu and picker helpers for worlds, backups, mod configs, logs, and common prompts.
Automation & Workflow Fixes
- Added
--yessupport to workshop sync, workshop archive, mod-list clearing, and backup restore so scripted flows do not hang on confirmations. - Updated maintenance to run workshop sync non-interactively, matching the cron-style examples in the docs.
- Tightened workshop sync so pre-2023 mod builds are skipped consistently instead of being copied into
Mods/.
Backup Safety
- Fixed restore correctness by switching rsync-based restore paths to checksum mode.
- Fixed same-second backup filename collisions so pre-restore safety backups cannot overwrite the archive you are trying to restore.
Portable/Public Release
- Reworked the toolkit into a project-root portable layout instead of assuming a fixed home-directory install.
- Made
steamcmd_pathdefault to./Tools/SteamCMD/steamcmd.sh. - Added support for project-relative paths in config so tracked examples stay machine-agnostic.
- Added
make steamcmd-localfor repo-local SteamCMD bootstrap. - Added
Scripts/env.example.shand improvedmake setupfor portable onboarding. - Added GitHub community health files, issue templates, PR template, and CI workflow for the public repo.
- Reframed the repo and README around the portable public edition.
Bug Fixes
- Fixed full backup and full restore to respect the capitalized
Logs/andBackups/layout. - Fixed diagnostics
auto_fixto recreate the actual repo directory structure instead of legacy lowercase paths. - Fixed monitor process detection so monitoring and health checks agree with the server start mode.
- Fixed
tmod-control.sh diagnosticsto run the full diagnostics script instead of the lightweight inline summary.
Inherited from the original tmodloaderserver line before the portable fork became the public repo.
Menu & UX
- Removed obsolete Download Mods and Sync Mods menu items in favor of the URL-to-
enabled.jsonflow. - Restructured all five menu pages for a better headless-server workflow.
- Expanded Monitoring with dashboard, health check, live monitor, log viewing, and console attach.
- Expanded Backup with inline restore and verify pickers, cleanup, and log viewing.
- Added Mod Configs editing from the Mods page.
- Added world import flow from pre-uploaded
.wldfiles.
Logging and Config
- Moved script logging to file-first behavior while keeping warnings and errors visible in the terminal.
- Added
--debugsupport throughTMOD_DEBUG=1for noisier child-script output. - Renamed the main directories to the capitalized tModLoader-style layout.
- Moved
STEAMCMD_PATHhandling intoserverconfig.txtassteamcmd_path=. - Added configurable log rotation settings and improved rotated log handling.
- Added
server_config_get()helper support in core scripts.
Cleanup
- Removed the whitelist system from the toolkit.
- Excluded
*.bakfrom git. - Started treating
Scripts/steam/mod_ids.txtas a local gitignored file with a tracked example template.
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.