← Back to Wiki
Game Servers / Hytale

Self-Hosting Hytale: Finding the Real Save Among Broken Containers

Self-hosting Hytale with the community indifferentbroccoli/hytale-server-docker image is straightforward on paper. Pull, mount a data folder, run. Nobody warns you what happens when that mount points at the wrong path. The server runs fine, players build for weeks, and every byte of it lands somewhere you will never think to look during a routine migration.

Share on X

The setup

The indifferentbroccoli/hytale-server-docker image is the go-to for self-hosting Hytale on Linux. A minimal compose file looks like this:

services:
  hytale:
    image: indifferentbroccoli/hytale-server-docker:latest
    network_mode: host
    environment:
      DOWNLOAD_ON_START: "false"   # see the version-pinning section below
      MAX_MEMORY: 3G
    volumes:
      - /opt/hytale/data:/home/hytale/server-files

That last line, the container-side mount path, is the whole story of this guide.

When "which save is the real one" isn't obvious

Migrating an existing Hytale server to new hardware turned up two candidate data directories. One was a single bind-mount, last written months earlier, holding a small, essentially untouched world. Empty mods list, no build history. The other was the debris of one troubleshooting session. Five separate containers, all created and destroyed within about half an hour, all pointed at a bind-mount that was now completely empty.

The obvious read was that the small, older save was the real one, since it was the only bind-mount with data in it. I migrated it over and brought the server up. The world was empty. No houses, nothing built, on a server that had clearly been played for a while before the migration.

BE WARNED: an empty bind-mount directory does not mean an empty world. Both candidate save locations got checked for real chunk and player data. Neither had any. The gameplay had to be somewhere else entirely. Which meant "the save lives in the folder the compose file points at" was wrong for this deployment, and had been wrong for as long as it existed.

The bind-mount path mismatch that caused it

Reading the image's own launch scripts, rather than trusting whatever bind-mount path an existing deployment happened to use, gave the answer. The server writes its data to /home/hytale/server-files inside the container. All five of the troubleshooting session's containers had their bind mount pointed at /server-files, missing the /home/hytale prefix entirely.

With DOWNLOAD_ON_START=true, each of those containers downloaded and ran a completely real server. Players connected, built, played for weeks. The bind mount was watching the wrong path, so none of that touched the host filesystem. Every byte went into the container's own internal writable layer, invisible to anyone looking at the host directory the compose file claimed to persist data to. The small, real-looking save from the other bind-mount was a near-total decoy. A separate, essentially never-played instance that happened to be the only one with anything visible on disk.

Recovering data trapped in a container's writable layer

The real world was still there, untouched inside one of the stopped troubleshooting containers. Stopped, not removed. docker cp pulls individual files out of a stopped container. For a whole directory tree it is easier to export the full container filesystem as a tarball and filter it to the path that matters:

docker export <stopped-container-name> | \
  tar -x -C <staging-dir> --strip-components=3 'home/hytale/server-files' \
  --exclude='home/hytale/server-files/.cache' \
  --exclude='home/hytale/server-files/downloader'

From there only the save data went onto the new host. universe/, mods/, whitelist.json, bans.json, permissions.json, config.json. The extracted server jar and assets stayed behind in favour of a current build. An old stopped container is very likely running a stale server version by the time you recover from it.

The generalizable move. When an image's launcher hardcodes an internal data path, verify what that path actually is by reading the image's own scripts. Never assume an existing bind-mount config is correct because it is already in production. A wrong path does not fail loudly. It quietly writes everything into a disposable layer instead of your persistent volume, and the symptom you eventually see, an "empty" world after a migration, looks nothing like its cause.

A crash that looked like a mod problem, wasn't

Once the real save was deployed with its full mod list, the server crash-looped on boot. Blaming the mods immediately was tempting. A stale mod incompatible with whatever server version shipped since the save was last played is a reasonable first guess. It turned out to be exactly that, for one of the seven mods. A hard ClassNotFoundException on an event class that no longer exists in the current server API. Removing that one mod stopped the crash-loop outright. A second, cosmetic decode warning on a few base-game armor assets remained from the rest of the mod set. It was not what crashed the server. Isolating the two symptoms, one fatal and one not, mattered more than reacting to the first error message in the log.

One detail in the boot log confirmed the recovered save was genuinely the real one, not a fresh world that happened to load without error. In-game elapsed time jumped from a fresh world's baseline straight to several in-game years, matching real play history rather than a clean slate.

A client-side gotcha worth knowing about

BE WARNED: "Server address is not correctly formatted" usually has nothing to do with the server. Hytale clients' "Add Server" screen wants the host and port in separate fields, and so do several similar dialogs on Bedrock-style clients. Paste a combined host:port string into a single address field and you get a generic formatting error that reads like a server-side problem. If the error persists after you split the fields correctly, delete the server entry and re-add it fresh. A stale or partially pasted previous attempt is far more likely than anything on the server.

The lesson that generalizes

When migrating any containerized game server, do not trust "the bind-mount directory has files in it" as proof you found the real data. Check the specific markers of real play. Real chunk counts, real elapsed time, real player history. Not the presence of some data. And when a container's launcher has an internal path convention, read the image's own source before you trust an existing deployment's configuration. A silently wrong path runs a perfectly healthy server for weeks while writing every bit of progress somewhere you will never think to back up.