Docker image to run a Terraria server with TShock. Focused on security and completely rootless.
2.1K
Features • Requirements • Quick Setup • Environment Variables • Dynamic Inventory • Volumes • Security
image for Terraria
servers. Designed with a focus on automation, data security, detailed monitoring, and is completely rootless.If you manage servers in any professional Docker environment, you know that visibility is everything.
This image was built for administrators who need reliable logs and dynamic configurations via environment variables, without sacrificing the simplicity of docker-compose.
Important
✔️ Working Docker installation ✔️ Basic knowledge of Docker.
If you want to test the container, use the shell command:
docker run --rm -di --terraria finallf/terraria:latest
The recommended way to run your server is using Docker Compose.
1 - Create a compose.yml file and adapt the volume paths to your environment, similar to this:
services:
terraria:
image: finallf/terraria:latest
container_name: terraria
stdin_open: true
tty: false
environment:
- SERVER_PASSWORD=yourpassword123
- WORLD_NAME=My World Terraria
volumes:
# Map these folders to persist your data on the host.
- ./terraria/config:/tshock/config
- ./terraria/logs:/tshock/logs
- ./terraria/crashes:/tshock/crashes
- ./terraria/plugins:/tshock/plugins
- ./terraria/worlds:/tshock/worlds
ports:
- "7777:7777" # Game Port
- "7878:7878" # REST API Port (Optional)
restart: unless-stopped
These are the minimum required settings.
For more refined settings, see the Environment Variables section for more information.
2 - Make sure that the folders on the host have the correct permissions for the UID, the container runs by default with UID 1000.
3 - To begin, run the following command in the terminal, in the directory containing the compose.yml file:
docker compose up -d
Once your Docker container is running, open your Terraria client and connect using the host's IP address and the default port 7777.
If this is the first time running the container, it may take a while due to world creation.
4 - You can also monitor the startup:
tail -f ./tshock/logs/container_init.log
Note
5 - "First Steps" Instructions (Admin Setup):
When starting the container for the first time, check the logs (docker logs terraria) to copy the Setup Code generated by TShock. You will need it in-game to use the /setup command.
You can configure the server behavior using the variables below in your compose.yml file or via the -e flag in docker run:
| Variable | Description | Default |
|---|---|---|
| LOG_INIT | If true, enable container logging, saving everything to the container_init.log file. | false |
| SERVER_PASSWORD | The server password required to join the server. | false |
| MAX_SLOTS | Maximum number of clients connected at once. | 8 |
| REST_API_ENABLED | If true, activate the REST API. | false |
| LOG_REST | If true, enables logging of REST API connections. | false |
| DISABLE_UUID_LOGIN | If true, prevents users from logging in with the client's UUID. | false |
| AUTO_SAVE | Enable or disable Terraria's built-in world auto save. | true |
| SSC_ENABLED | If true, enables server-side character, causing client data to be saved on the server instead of the client. | false |
| SSC_SAVE | How often SSC should save, in minutes. | 5 |
| PLAYER_APPEARANCE | If true, it allows players to retain the local appearance of their characters in SSC. | false |
| STARTINGINVENTORY | If true, adds some items to the Inventory for new players when SSC is enabled.Click here 👆 for more information. | false |
| WORLD_NAME | Give your World a friendly name. | (Empty) |
| WORLD_FILE | Specifies a name for the world file. | terraria_world.wld |
| AUTO_CREATE | Creates the world file with the specified size (1: Small, 2: Medium, 3: Large). | 1 |
| DIFFICULTY | Sets the world's difficulty (0: normal, 1: expert, 2: master, 3: journey). This only affects new worlds. | 0 |
| WORLD_EVIL | Sets the world's evil state (random, corrupt, crimson). | random |
| SEED | Specifies the world seed when using -autocreate. | random |
| FORCE_UPDATE | If true, prevents the server from entering hibernation mode when there are no players. | false |
| MOTD | Sets the Message of the Day. | (Empty) |
| SECURE | If true, activates the base game's "antispam" feature. | false |
| LANG | Sets the server language (en-US, de-DE, it-IT, fr-FR, es-ES, ru-RU, zh-Hans, pt-BR, pl-PL). | en-US |
| TZ | Set your local time zone. - See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones | UTC |
Tip
Click here 👆 for a complete example of the compose.yml and .env files:
compose.yml:
networks: terraria: external: false name: terraria services: terraria: image: finallf/terraria:latest container_name: terraria user: 1000:0 stdin_open: true tty: false environment: - LOG_INIT=true - SERVER_PASSWORD=123456 - MAX_SLOTS=16 - REST_API_ENABLED=true - LOG_REST=true - DISABLE_UUID_LOGIN=true - AUTO_SAVE=true - SSC_ENABLED=true - SSC_SAVE=1 - PLAYER_APPEARANCE=true - STARTINGINVENTORY=true - WORLD_NAME=ReloadeD Server - WORLD_FILE=my_world.wld - AUTO_CREATE=3 - DIFFICULTY=2 - WORLD_EVIL=crimson - SEED=fortheworthy - FORCE_UPDATE=true - MOTD=Welcome to My Server! - SECURE=true - LANG=pt-BR - TZ=America/Sao_Paulo volumes: # TShock paths for settings, SQLite database, logs, crashes, plugins, and world files. - ${SSD}/config:/tshock/config - ${SSD}/logs:/tshock/logs - ${SSD}/crashes:/tshock/crashes - ${SSD}/plugins:/tshock/plugins - ${SSD}/worlds:/tshock/worlds ports: - "7777:7777" # The port used by the REST API. - "7878:7878" networks: - terraria stop_grace_period: 30s restart: unless-stopped.env
SSD=/path/on/host/terraria
Caution
Don't forget to adjust the options to suit your specific situation.
The Dynamic Inventory system allows you to define which items new players will receive when they first join the server, without needing to edit files manually.
The script processes this information atomically on boot using jq.
🔹 Option 1: Basic Kit (Quick Mode)
- If you set the variable to true, the server automatically injects a default starter kit:
(99 Glowsticks, 99 Ironskin Potions, 1 Aglet, 1 Ice Mirror, 1 Gravedigger's Shovel).
environment:
- STARTINGINVENTORY=true
🔹 Option 2: Customized Kit (Advanced Mode)
- To define specific items, use the compact string format:
ID, PREFIX, QUANTITY separated by a colon ( : ).
Syntax: netID,prefix,stack:netID,prefix,stack:...
Practical Example:
For players to start with a Platinum Axe (ID: 3482), 10 Torches (ID: 8), and 5 Lesser Healing Potion (ID: 28):
environment:
- STARTINGINVENTORY=3482,0,1:8,0,10:28,0,5
🔹 Option 3: Removing an item
- You can also remove any item from the list:
Using 'remove:ID'
Syntax: remove:netID
Practical Example:
We will remove the Platinum Axe (ID: 3482)
environment:
- STARTINGINVENTORY=remove:3482
Tip
You can find the complete list of Terraria item netIDs on the Official Wiki.
To ensure your server maintains its progress after reboots or image updates, it's necessary to map the 5 main volumes. Below, we detail the content and purpose of each:
/tshock/config
config.json, sscconfig.json, tshock.sqlite).sscconfig.json file resides here and is where the initial inventory is managed./tshock/logs ( optional )
container_init.log file and native TShock logs.jq ) and view server errors without needing to access the Docker console./tshock/crashes ( optional - legacy )
.dmp files into a specific network folder or volume./tshock/plugins ( optional )
/tshock/worlds
.wld ) and automatic backup folders.Tip
Host OrganizationWhen configuring your server, we recommend creating a folder structure that mirrors the volumes to facilitate backups:
/path/on/host/terraria/ ├── config/ ├── logs/ ├── crashes/ ├── plugins/ └── worlds/
To ensure the highest level of security, this image was built following the Rootless standard. This means that the TShock server and all internal scripts do not run as administrator (root) within the container.
This prevents game vulnerabilities from compromising your physical server (host), but requires correct configuration of the permissions for the mapped folders in your compose.yml file.
🔹 How does a static UID/GID work
By default, the container's internal process runs with a static User ID (e.g., UID 1000) and belongs to the Root Group (GID 0).
0 is a recommended practice (adopted by platforms like Red Hat OpenShift) that allows for flexibility. The container does not require that the user who owns the folder on the host be exactly 1000, as long as the folder's group allows read and write access.🔹 How to prepare your folders on the Host
Before starting the container for the first time, you need to ensure that the directory where the volumes will be saved allows writing.
compose.yml file to match the UID of your host (replace 1000 with your UID):services:
terraria:
user: 1000:0
sudo chown -R 1000:1000 ./terraria
0:sudo chgrp -R 0 ./terraria
sudo chmod -R g+rwX ./terraria
Whichever method you choose, the container will be able to write logs, worlds, and sscconfig.json without permission conflicts, keeping Rootless security intact.
This image was designed following the philosophy of total observability. Every step of the initialization and every command sent to the server is recorded with chronological precision.
📁 Log Locations
To persist logs outside the container, map a volume in your compose.yml file:
volumes:
- /path/to/host/logs:/tshock/logs
The main file will be container_init.log, where you will find the complete trace of:
🕒 Precision Timestamps
Unlike standard logs, our system prefixes each line with a customizable timestamp:
[2026-04-05 14:30:05] [INFO] Items successfully added to starting inventory.
This allows you to cross-reference error information with events from your file system or network on the host.
📟 Console-based interactivity (Named Pipe)
Since the server runs in the background to allow monitoring, you don't use docker attach.
To send commands (such as kick, ban, or save), use the built-in Named Pipe:
Example of a command via the terminal:
docker exec -i [container_name] sh -c 'echo "say Hello Terrarians!" > /tmp/terraria_input'
🛑 Graceful Shutdown
When you send a docker stop command, the container captures the interrupt signal and automatically executes a save script.
This ensures that the world's progress is written to disk before the process is terminated, preventing the dreaded "roll-back" of items.
If this project has helped you in any way, consider buying me a coffee! Your donation helps keep the updates and documentation current.
🇧🇷 Se este projeto te ajudou de alguma forma, considere me pagar um café! Sua doação ajuda a manter as atualizações e a documentação.
| 🌎 GitHub Sponsors | ||
|---|---|---|
| You can support me through GitHub Sponsors. | 🇧🇷 Escaneie o QR Code:![]() | Click or scan the QR code:![]() |
🇧🇷 Ou utilize a Chave Pix (Copia e Cola):
25d1d528-df10-4005-bb28-2acf89706243
Note
If you have any questions, check out this guide on how to contribute on GitHub: 📖
- Fork the project.
- Create a new branch with your changes:
git checkout -b my-feature- Save the changes and create a commit message describing what you did:
git commit -m "feature: My new feature"- Send your changes:
git push origin my-feature
The following tools were used in the construction of the project:
💜 Thank you to everyone who contributed to the improvement of this project 😊
Warning
This project is licensed under: GPL-3.0 license.
Content type
Image
Digest
sha256:218ad6843…
Size
143.1 MB
Last updated
6 months ago
docker pull finallf/terraria