← Back to Wiki
Self-Hosting / Game Servers

Run a "Windows-Only" Dedicated Server on Plain Linux

Some games ship an "official" self-hosting tool that flatly requires Windows plus Hyper-V — nested virtualization and all. Open the box before believing that. Often the actual payload is just a portable Linux disk image with a full container orchestration stack already inside it, and the Windows/Hyper-V wrapper is only there because that's what the publisher's own tooling happened to be built on top of.

Look inside the box first

A modern "dedicated server" for a live-service game is rarely a single executable anymore — it's often a small fleet of coordinated services (a database, a message queue, matchmaking/gateway logic, the actual game-world process) bundled together with something like Kubernetes to manage them. If the official installer requires Windows, check what it's actually shipping before assuming you need a Windows box at all: publishers have said as much publicly for at least one game using this pattern — Linux-direct hosting works fine, they just don't provide official instructions for it, because their own packaging tool happens to require Windows/Hyper-V to unpack it.

Extract and boot the real payload directly

Once you've confirmed the official installer is really just unpacking a self-contained disk image (VHDX, in the case this is based on), convert it to your own hypervisor's format and boot it directly:

qemu-img convert -f vhdx -O qcow2 server-disk.vhdx server-disk.qcow2
qemu-img resize server-disk.qcow2 80G

Boot that directly under KVM (or whatever your hypervisor is) — no Windows, no Hyper-V, no nested virtualization anywhere in the chain.

Gotcha: networking must be configured before first boot, not after — the orchestration layer inside the image typically reads its network config once at first startup and bakes the result into its own internal settings. If it boots once with the wrong (e.g. DHCP-assigned) address, pod/container networking breaks and won't self-correct just by changing the config later. Mount the disk offline first (via NBD) and fix the network config, DNS, and kernel modules before ever powering it on:
modprobe nbd max_part=8
qemu-nbd --connect=/dev/nbd0 server-disk.qcow2
vgchange -ay vg0
mount /dev/mapper/vg0-lv_root /mnt
# edit /mnt/etc/network/interfaces for a static IP
# check /mnt/etc/resolv.conf -- shipped images often point at their original
# host environment's internal DNS, which is dead anywhere else
umount /mnt; vgchange -an vg0; qemu-nbd --disconnect /dev/nbd0
A cert-manager-style TLS error on first world creation usually self-heals. Something like x509: invalid signature: parent certificate cannot sign this kind of certificate right after first boot looks alarming but is typically just clock skew during early startup before NTP has synced. Wait a few minutes and retry the same command rather than troubleshooting further.
Gotcha: a custom startup wrapper may cache the node's own IP at boot only. If you ever change the host's network config after the fact, restarting just the application layer isn't enough — some of these images have their own startup script that reads network config once at their own service's startup and bakes it into internal flags, completely separate from the container orchestrator's restart cycle. Restarting only the containers/pods leaves the stale IP baked in indefinitely. Restart the underlying service itself first, then the application layer on top of it.

Self-hosting the server doesn't mean you're independent from the publisher

This is the part that actually caused a real, confusing bug — worth walking through in full because the debugging process is the useful part, including two wrong turns along the way.

The symptom: a player transferred their character onto a self-hosted world and then got a generic "Claim failed, try again later" error trying to redeem a returning-player reward package — even after waiting over an hour, as the game's own message suggested.

First finding, and it's an important one for anyone running one of these: a self-hosted world is not actually isolated from the publisher's live infrastructure. The game-world processes make real outbound HTTPS calls to the publisher's own cloud backend for entitlements, transfers, and reward systems — none of that is simulated locally just because you're hosting the "dedicated server" part yourself. The actual failure was findable in the **game process's own logs**, in a category clearly labeled as talking to the publisher's live services, not anywhere in the hosting/orchestration layer's own logs:

LogFuncomLiveServices: Error: https://[publisher-live-service-host]/api/ClaimSystem_GrantPacksForCharactersServer... Failed: Request Timeout or null response
LogDuneCharacter: Error: ...GrantReturnRewards... Failed to grant returning-player award packs for the character...
Wrong turn #1: assumed it was a transient network blip. Called the exact same endpoint directly from inside the running game process and got a clean, fast response — looked like the failure had simply passed. It recurred identically on the player's very next real attempt, proving a single clean manual test doesn't confirm anything is actually fixed. Only a real repeat attempt does.
Wrong turn #2: guessed it might be a fundamental self-hosting limitation — that the publisher's backend silently rejects reward claims from unofficial/self-hosted servers as an anti-abuse measure. This was never actually confirmed, and turned out to be wrong — worth naming explicitly, because it's exactly the kind of plausible-sounding theory that's easy to accept without real evidence, especially once one wrong theory has already burned time.

The actual answer, found through the publisher's own patch notes and player community reports, not guessed: this was a documented bug. Transferring a character to a new world without first logging into the character's origin world to view the reward there resets its eligibility. The publisher had already shipped a fix for the common case months earlier — but it has a real edge case: if the character's origin world had already been shut down by the time of the transfer, there's nothing left to "log into first," and the fix can't retroactively apply. The only resolution other players in that exact situation reported success with was an official support ticket requesting manual compensation — not anything fixable through local server configuration.

Checking your build is current without needing real credentials

If the disk image is distributed through a platform like Steam rather than a bespoke publisher download, you likely already have a lightweight way to check whether you're running the latest available build, without a full re-download or even a real login:

steamcmd +login anonymous +app_info_print <appid> +quit

Look for the branches → public → timeupdated field in the output (a Unix timestamp — pipe it through date -u -d @<value> to read it) and compare it against when you actually built your deployment. If the publisher's build predates your own deployment date, you're already current and a stale binary can be ruled out as a cause — worth checking before assuming an obscure bug needs a version bump to fix.

General lesson from the whole investigation: your own infrastructure being completely healthy doesn't mean a gameplay bug is actually happening inside your infrastructure. Check the real game process's own logs — specifically whatever category handles calls to the publisher's live-service backend — before assuming anything about your own setup. And hold your first plausible-sounding theory loosely: this one took three attempts (transient blip, then an unconfirmed self-hosting-limitation guess, then the real documented bug found through actual research) before landing on the truth.