A production-ready, secure Docker implementation for the Hytale Server
This project provides a container environment. It is designed with some security in mind (hopefully), running as a non-root user with minimal kernel capabilities.
- Rootless Runtime: Runs as
uid:1001(hytale) for isolation. - Automated Install: Detects missing server files and fetches the latest patchline automatically.
- CLI Wrapper: Includes
hytale-clifor sending commands without entering the container. - Git-Friendly: Pre-configured
.gitignorefor server data.
- Docker & Docker Compose
bash(for the CLI wrapper)
Clone the repository and build the image.
git clone https://github.com/Dari-OS/hytale-container.git
cd hytale-container
docker compose up -d --build
Authentication is a two-step process: first for the Installer (to download files), and second for the Server (to host the game).
- Start the container and view the logs:
docker logs -f hytale_server
-
Look for the Installer OAuth message:
Please visit the following URL to authenticate:
https://oauth.accounts.hytale.com/oauth2/device/verify?user_code=<YOUR_CODE>
Or visit the following URL and enter the code:
https://oauth.accounts.hytale.com/oauth2/device/verify
Authorization code: <YOUR_CODE> -
Visit the link and approve the session.
-
The server will download assets (~1.4GB) and boot automatically.
Once the download finishes, the server will start. You will see a log entry: No server tokens configured. Use /auth login to authenticate.. You must authenticate the running server instance.
- Use the CLI tool to initiate the Device Flow:
./hytale-cli auth login device
- The logs (or your console) will display a new URL and Code. Visit the link to approve.
- Crucial: By default credentials are stored in memory and lost on restart. Enable persistence to save them to the volume:
./hytale-cli auth persistence Encrypted
Note: Credentials are cached in the volume. You do not need to repeat this step on restarts.
Do not use docker attach. Instead, use the included wrapper script. This ensures the input stream is handled correctly via the named pipe.
Interactive Mode:
./hytale-cli
Type commands normally. Press Ctrl+C to exit the tool (server remains running).
Single Command:
./hytale-cli op Self
To update the server to the latest version of the selected patchline:
- Open
docker-compose.yml. - Set
UPDATE_ON_BOOT=true. - Restart the container:
docker compose restart
- Recommended: Set it back to
falseafter the update to prevent re-downloading on every restart.
Environment variables can be set in docker-compose.yml.
| Variable | Default | Description |
|---|---|---|
HYTALE_PATCHLINE |
release |
The update branch (release, etc). |
UPDATE_ON_BOOT |
false |
If true, re-runs the installer on every start. |
SERVER_PORT |
5520 |
The UDP port the server listens on. |
JAVA_MS |
2G |
Initial Java heap size. |
JAVA_MX |
4G |
Maximum Java heap size. |
TERM |
xterm |
Required for terminal emulation support. |
.
├── docker-compose.yml # Container orchestration
├── Dockerfile # Base image definition (Eclipse Temurin 25)
├── docker-entrypoint.sh # Startup logic, permissions fix, and pipe handler
├── hytale-cli # Host-side wrapper script
└── server/ # Volume mount (Contains world data & config)
The server/ directory is locally mounted to persist data. To back up your world, simply zip this folder.
This setup adheres to container security best practices:
- User Isolation: The container entrypoint starts as
rootonly to fix volume permissions (chown) and create the named pipe, then immediately drops privileges to thehytaleuser usinggosu. - Capability Dropping: The
docker-compose.ymldrops all Linux capabilities (cap_drop: ALL) and selectively adds back only what is strictly necessary (CHOWN,SETUID,SETGID). - No New Privileges:
security_opt: no-new-privileges:trueprevents privilege escalation attacks (e.g., sudo/suid binaries) inside the container.
Nothing happens after docker compose up -d --build:
Make sure to follow the tutorial and authenticate!
Stuck on downloading latest ("release" patchline) to "/data/hytale/output.zip":
Don't worry, it is not stuck it is just downloading. Sadly the progress does not get updated.
This is some weird behaviour caused by docker logs so be patient.