Running a game server in a container is a good idea for reasons that have nothing to do with performance: a clean filesystem per server, a reproducible start command, and the ability to move the whole thing to another host without reinstalling anything.
It also introduces one failure mode that catches people, and it catches them silently.
The limit is enforced by killing
Docker's resource constraint documentation describes what --memory does. When a container exceeds its memory limit, the kernel's out-of-memory killer terminates a process inside it.
There is no warning, no gradual slowdown and no swap by default. The process is running and then it is not.
For a Java game server this matters more than for most workloads, because the JVM is designed to use everything it is allowed and to assume that "allowed" means "available".
Heap is not the whole process
A JVM with -Xmx4G does not use 4GB. It uses 4GB of heap, plus metaspace, plus a thread stack for every thread, plus direct byte buffers (which Netty, and therefore every Minecraft server, uses heavily), plus the garbage collector's own structures, plus the JVM itself.
Set the container to 4GB and the heap to 4GB and the container will be killed during ordinary operation. Not under load, not during a spike; during whatever moment the overhead crosses the line.
The heap has to be comfortably under the container limit. How far under depends on thread count and plugin behaviour, and the only way to know for your server is to watch it.
Why the log says nothing
This is the part that costs people days. An OOM kill is not an exception. The process receives SIGKILL, which cannot be caught, so nothing gets written: no stack trace, no shutdown message, no final save.
The console shows normal operation and then ends mid-line. People go looking for a plugin that crashes and find nothing, because nothing crashed.
The evidence is outside the container. docker inspect reports OOMKilled: true on the stopped container, and the host's kernel log records the kill. Neither is anywhere the game's own logs would lead you, which makes this one of the genuinely hard cases in tracing a crash.
Swap, and why turning it on is usually wrong
Docker exposes --memory-swap separately, and the arithmetic surprises people: it is the total of memory plus swap, not the swap on its own. Setting --memory=4g --memory-swap=4g means four gigabytes total and no swap at all, which is what most people actually want without realising they have asked for it.
Giving a game server real swap is worse than the kill it prevents. A JVM whose heap is partly on disk does not run slowly in the way a web server does — the garbage collector walks the whole heap periodically, so a collection touches the swapped pages and the server freezes for seconds at a time. Players read that as the server being broken, and they are not far wrong.
A clean kill and a fifteen-second restart is the better outcome. That is an unusual sentence to write and it is specific to workloads that must be responsive rather than merely finish.
CPU limits behave differently, and better
--cpus throttles rather than kills. A container over its CPU allowance is slowed, not terminated, which is why CPU limits produce lag complaints and memory limits produce mysteries.
That asymmetry is worth holding onto: if the server is slow, look at CPU and at what the server is doing. If the server vanishes, look at memory and at the host.
What containers do not fix
They do not give you cores you did not have. Four containers on a two-core box contend exactly as four processes would, and the isolation is in the filesystem rather than the silicon, which is the same reality behind every shared hosting plan, whether it says Docker anywhere or not.
They also do not make a server portable if the data lives inside the container. A world written to the container's own filesystem is gone when the container is replaced. Volumes exist for this and skipping them is a mistake that is invisible until the first time you rebuild — at which point it becomes the same conversation as any other backup that was theoretical.
A working arrangement
Give the container a memory limit. Set the heap meaningfully below it. Mount the world and the configs as volumes. Log to stdout so the container runtime collects it. Check OOMKilled first whenever a server disappears without explanation.
That last habit is the one worth building, because the alternative is searching a log for an error that was never written, and the broader lesson, that the cause is often outside the thing that failed, is the same one that makes monitoring the host rather than the game worth the setup time.
- Tags
- dockerhostingtroubleshooting
- Share
Published · 4 min read