Why Run Minecraft on Kubernetes?
There are a lot of ways to run a Minecraft server. You can pay for a hosted one, spin up a VM, or just run it on your desktop. But if you already have a Kubernetes cluster sitting in your homelab, why not use it?
Running Minecraft on Kubernetes gives you a few things for free: automatic restarts if the server crashes, resource limits so it doesn’t eat your entire node, persistent storage so your world survives redeployments, and the ability to manage everything through GitOps alongside the rest of your homelab.
The Setup
My homelab runs on a Kubernetes cluster managed through Helm charts. Each service gets its own chart with a namespace, deployment, service, persistent volume claim, and ingress configuration. Minecraft fits neatly into this pattern.
For the server image, I use itzg/minecraft-server, a well-maintained Docker image that supports Java Edition, Bedrock, Paper, Spigot, and just about every server type out there. It handles EULA acceptance, server properties, and plugin installation through environment variables, which makes it easy to configure declaratively in a Helm chart.
The Helm Chart
The chart follows the same structure as every other service in my homelab:
helm-minecraft/
Chart.yaml
values.yaml
values.proxmox.yaml
templates/
namespace.yaml
deployment.yaml
service.yaml
pvc.yaml
tcp-ingress-configmap.yaml
The values.yaml defines the server configuration: Paper server type, survival mode, 20 max players, 10 chunk view distance, and plugins pulled from Modrinth and SpigotMC. Resource limits are set to 2Gi-4Gi of memory and up to 2 CPU cores, which is more than enough for a small server with friends.
For storage, a 10-20Gi persistent volume claim on the local-path storage class keeps the world data safe across pod restarts. Since the deployment uses strategy.type: Recreate, the old pod is fully stopped before the new one starts, which avoids any file locking issues with the world save.
Exposing the Server
Minecraft uses TCP on port 25565, so the typical HTTP ingress setup does not work here. Instead, the chart creates a ConfigMap in the ingress-nginx namespace that tells the nginx ingress controller to forward TCP traffic on port 25565 to the Minecraft service:
data:
"25565": "minecraft/minecraft-server:25565"For local network access, a NodePort service on port 30565 works as a simpler alternative. Players on the same network can connect directly to the node’s IP.
Plugins
One of the nice things about the itzg image is that plugins can be declared in the deployment config and downloaded automatically on startup. I run three:
- EssentialsX for basic server management commands
- LuckPerms for permissions
- VaultUnlocked as a permissions API bridge
These are pulled from SpigotMC and Modrinth by the container at boot, so updating a plugin version is just a values change and a redeploy.
Takeaway
If you already run a Kubernetes homelab, adding Minecraft is straightforward. The itzg Docker image handles most of the server complexity, Helm keeps the configuration declarative and version controlled, and Kubernetes handles the lifecycle. The whole thing is a handful of YAML files that live in the same repo as the rest of the homelab.