Self-hosting Hytale with the community indifferentbroccoli/hytale-server-docker image
is straightforward on paper — pull, mount a data folder, run. The part nobody warns you about is 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'll never think to look during a routine migration.
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.
Migrating an existing Hytale server to new hardware turned up two candidate data directories. One was a single bind-mount, last written months earlier, containing a small, essentially-untouched world (empty mods list, no real build history). The other was the debris of a single 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: the small, older save was the "real" one, since it was the only bind-mount that actually had data in it. Migrated it over, brought the server up — and the world was empty. No houses, nothing built, despite the server having clearly been played on for a while before the migration.
Reading the image's own launch scripts directly (rather than trusting whatever bind-mount path an existing
deployment happened to use) revealed the actual answer: the server writes its data to
/home/hytale/server-files inside the container. The troubleshooting session's five containers
were all configured with 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. But because the bind mount was watching the wrong path,
none of that ever touched the host filesystem — every byte of it was written into the container's own
internal writable layer, invisible to anyone looking at the host directory the compose file claimed to be
persisting data to. The small, "real-looking" save from the other bind-mount was a near-total decoy — a
separate, essentially never-played instance that just happened to be the only one with anything visible on
disk.
The actual world was still there, sitting untouched inside one of the stopped (not removed) troubleshooting
containers. docker cp works for pulling individual files back out of a stopped container, but
for recovering a whole directory tree it's easier to export the full container filesystem as a tarball and
filter it down 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 actual save data (universe/, mods/, whitelist.json,
bans.json, permissions.json, config.json) got copied onto the new host
— the extracted server jar and assets were deliberately left behind in favor of a current build, since an old
stopped container is very likely running a stale server version by the time you're recovering from it.
Once the real save was deployed with its full mod list, the server crash-looped on boot. It was tempting to
blame the mods immediately — a stale mod incompatible with whatever server version had shipped since the save
was last played is a completely reasonable first guess. It turned out to be exactly that, but only for
one of the seven mods: a hard ClassNotFoundException on an event class that no longer
exists in the current server API. Removing that single 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, but it wasn't what was
actually crashing the server — isolating the two symptoms (one fatal, one not) mattered more than reacting to
the first error message seen in the log.
Confirming the recovered save was genuinely the real one, not a fresh world that happened to load without error, came down to one detail in the boot log: in-game elapsed time jumping from a fresh world's baseline straight to several real in-game years, matching actual play history rather than a clean slate.
host:port string into a single address
field throws a generic formatting error that reads like a server-side problem but isn't. If the error persists
after splitting the fields correctly, delete the server entry and re-add it fresh — a stale or
partially-pasted previous attempt is a far more likely cause than anything on the server.
When migrating any containerized game server, don't trust "the bind-mount directory has files in it" as proof you've found the real data — check for the specific markers of actual play (real chunk counts, real elapsed time, real player history), not just the presence of some data. And when a container's launcher has an internal path convention, read the image's own source before trusting an existing deployment's configuration — a silently wrong path can run a perfectly healthy server for weeks while writing every bit of progress somewhere you'll never think to back up.