Skip to content

Repository files navigation

Kubernetes Minecraft Server

A persistent Minecraft Java Edition server running on Kubernetes with a Paper server, persistent world storage, whitelist enforcement, and public access through a Playit tunnel.

No inbound router port forwarding is required.

Features

  • Minecraft Java Edition server
  • Paper server implementation
  • Kubernetes StatefulSet
  • Persistent world storage using a PersistentVolumeClaim
  • Internal Kubernetes Services
  • Public access through Playit
  • No router port forwarding
  • Mojang online authentication
  • Whitelist enforcement
  • RCON disabled
  • CPU and memory resource controls
  • Startup and readiness probes
  • Automatic Pod recovery
  • Separate Kubernetes Secret for the Playit credential

Architecture

Minecraft Clients
        │
        │ Public Minecraft connection
        ▼
Playit Public Endpoint
        │
        │ Outbound tunnel
        ▼
Playit Agent Deployment
        │
        │ TCP 25565
        ▼
Minecraft ClusterIP Service
        │
        ▼
Paper StatefulSet
        │
        ▼
PersistentVolumeClaim
        │
        ▼
Worlds, configuration, player data, and plugins

Repository Layout

.
├── 00-namespace.yaml
├── 01-minecraft-pvc.yaml
├── 02-minecraft-services.yaml
├── 03-minecraft-statefulset.yaml
├── 04-playit-secret.yaml.example
├── 05-playit-deployment.yaml
├── .gitignore
└── README.md

Requirements

  • A working Kubernetes cluster
  • kubectl configured for the cluster
  • A compatible local-path StorageClass
  • At least 4 CPU cores available to the Kubernetes node
  • At least 8 GiB of memory available to the Kubernetes node
  • Minecraft Java Edition
  • A Playit account
  • A Playit agent secret

The default resource configuration is intended for a small private server with approximately 4–8 active players.

Default Server Configuration

Minecraft version:       26.2
Server implementation:   Paper
Java version:            Java 25
Minecraft JVM heap:      3 GiB
Container memory limit:  4 GiB
CPU request:             1 core
Persistent storage:      20 GiB
Maximum players:         10
View distance:           8
Simulation distance:     6
Game mode:               Survival
Difficulty:              Normal

Adjust these values according to your cluster capacity and expected player count.

1. Clone the Repository

git clone git@github.com:calico88x/minecraft-kubernetes.git
cd minecraft-kubernetes

2. Configure the Minecraft Whitelist

Open:

vim 03-minecraft-statefulset.yaml

Find:

- name: WHITELIST
  value: ""

Add the permitted Minecraft Java Edition usernames as a comma-separated list:

- name: WHITELIST
  value: "PlayerOne,PlayerTwo,PlayerThree"

Whitelist enforcement is enabled by default:

- name: ENABLE_WHITELIST
  value: "TRUE"

- name: ENFORCE_WHITELIST
  value: "TRUE"

The empty default whitelist intentionally causes the server to fail closed. Add at least one username before attempting to join.

3. Create the Playit Secret

Copy the example file:

cp 04-playit-secret.yaml.example 04-playit-secret.yaml

Restrict its permissions:

chmod 600 04-playit-secret.yaml

Edit it:

vim 04-playit-secret.yaml

Replace the placeholder:

apiVersion: v1
kind: Secret
metadata:
  name: playit-secret
  namespace: minecraft
type: Opaque
stringData:
  SECRET_KEY: "YOUR_PLAYIT_SECRET_HERE"

The real 04-playit-secret.yaml file is excluded by .gitignore and must never be committed.

4. Deploy the Kubernetes Resources

Apply the manifests in order:

kubectl apply -f 00-namespace.yaml
kubectl apply -f 01-minecraft-pvc.yaml
kubectl apply -f 02-minecraft-services.yaml
kubectl apply -f 03-minecraft-statefulset.yaml
kubectl apply -f 04-playit-secret.yaml
kubectl apply -f 05-playit-deployment.yaml

5. Verify the Deployment

Check the Pods, Services, StatefulSet, Deployment, and PVC:

kubectl get statefulset,deployment,pods,services,pvc \
  --namespace minecraft

Expected Pod state:

NAME                         READY   STATUS    RESTARTS
minecraft-0                  1/1     Running   0
playit-xxxxxxxxxx-xxxxx      1/1     Running   0

The PVC should show:

STATUS   CAPACITY   ACCESS MODES
Bound    20Gi       RWO

6. Verify Minecraft Startup

Read the Minecraft logs:

kubectl logs minecraft-0 \
  --namespace minecraft \
  --tail=100

A successful startup includes:

Starting Minecraft server on *:25565
Done (...)! For help, type "help"

Follow the logs continuously:

kubectl logs minecraft-0 \
  --namespace minecraft \
  --follow

7. Verify Internal Kubernetes Networking

Run a temporary test Pod:

kubectl run minecraft-test \
  --namespace minecraft \
  --rm \
  --stdin \
  --tty \
  --restart=Never \
  --image=nicolaka/netshoot \
  -- nc -vz minecraft.minecraft.svc.cluster.local 25565

Expected result:

Connection to minecraft.minecraft.svc.cluster.local 25565 port [tcp/*] succeeded!

8. Verify the Playit Agent

Check the Playit logs:

kubectl logs deployment/playit \
  --namespace minecraft \
  --tail=50

A connected agent should report:

playit connected

An IPv6 message such as this may appear when the Kubernetes environment does not provide outbound IPv6 connectivity:

Network unreachable

This is not necessarily fatal if the agent connects successfully over IPv4.

9. Create the Playit Minecraft Tunnel

In the Playit dashboard:

  1. Confirm that the Kubernetes Playit agent is online.
  2. Create a new tunnel.
  3. Select Minecraft Java.
  4. Set the local port to:
25565
  1. Set the local destination to the Kubernetes minecraft Service.

Find the current Service IP:

kubectl get service minecraft \
  --namespace minecraft

Example:

NAME        TYPE        CLUSTER-IP      PORT(S)
minecraft   ClusterIP   10.43.224.98    25565/TCP

Use that ClusterIP as the Playit local IP and use port 25565.

Do not use:

127.0.0.1

The Playit agent and Minecraft server run in separate Pods, so 127.0.0.1 would point back to the Playit container itself.

Playit will provide a public hostname after the tunnel is created.

10. Connect from Minecraft

In Minecraft Java Edition:

  1. Open Multiplayer.
  2. Select Add Server.
  3. Enter any server name.
  4. Enter the public hostname provided by Playit.
  5. Join the server.

Do not rely on Scanning for games on your local network. LAN discovery does not cross the Kubernetes and Rancher Desktop virtual networks.

The Minecraft client version must be compatible with the server version configured in:

- name: VERSION
  value: "26.2"

Managing the Server

Check server status

kubectl get pods \
  --namespace minecraft

Check resource usage

kubectl top pod minecraft-0 \
  --namespace minecraft
kubectl top nodes

Restart Minecraft

kubectl rollout restart statefulset/minecraft \
  --namespace minecraft

Wait for Minecraft to become ready

kubectl rollout status statefulset/minecraft \
  --namespace minecraft

Restart Playit

kubectl rollout restart deployment/playit \
  --namespace minecraft

View Minecraft logs

kubectl logs minecraft-0 \
  --namespace minecraft \
  --follow

View Playit logs

kubectl logs deployment/playit \
  --namespace minecraft \
  --follow

Adding Players to the Whitelist

Edit:

vim 03-minecraft-statefulset.yaml

Update:

- name: WHITELIST
  value: "PlayerOne,PlayerTwo,PlayerThree"

Apply the updated StatefulSet:

kubectl apply -f 03-minecraft-statefulset.yaml

Wait for the rollout:

kubectl rollout status statefulset/minecraft \
  --namespace minecraft

The manifest uses:

- name: EXISTING_WHITELIST_FILE
  value: "SYNCHRONIZE"

This keeps the persisted Minecraft whitelist synchronized with the usernames declared in the StatefulSet.

Persistent Storage

Minecraft data is stored in the minecraft-data PVC and mounted inside the container at:

/data

This includes:

  • Overworld data
  • Nether data
  • End data
  • Player data
  • Whitelist
  • Server properties
  • Paper configuration
  • Plugins
  • Logs

Restarting or replacing the Minecraft Pod does not delete the world.

Do not delete the PVC unless you intentionally want to delete the persistent server data.

Check the PVC:

kubectl get pvc \
  --namespace minecraft

Temporarily Stopping the Server

Scale Minecraft to zero:

kubectl scale statefulset minecraft \
  --namespace minecraft \
  --replicas=0

Stop the Playit agent:

kubectl scale deployment playit \
  --namespace minecraft \
  --replicas=0

The Kubernetes resources and Minecraft PVC remain intact.

Start Minecraft again:

kubectl scale statefulset minecraft \
  --namespace minecraft \
  --replicas=1

Start Playit again:

kubectl scale deployment playit \
  --namespace minecraft \
  --replicas=1

Security

The default configuration includes the following controls:

  • Mojang online authentication enabled
  • Whitelist enabled
  • Whitelist enforcement enabled
  • RCON disabled
  • Playit credential stored in a Kubernetes Secret
  • No inbound router port forwarding
  • Separate Playit and Minecraft Pods
  • Restricted Playit container resources
  • Kubernetes Service used for internal routing
  • Persistent data separated from the Pod lifecycle

Never commit:

  • 04-playit-secret.yaml
  • Playit agent credentials
  • Personal access tokens
  • Private keys
  • Real .env files
  • World backups containing sensitive player information

Resource Considerations

The default Minecraft container requests:

resources:
  requests:
    cpu: "1"
    memory: "3Gi"
  limits:
    memory: "4Gi"

No CPU limit is configured for Minecraft. This allows the game server to use additional CPU during chunk generation without being throttled by a restrictive Kubernetes CPU quota.

The JVM is configured with:

- name: INIT_MEMORY
  value: "1G"

- name: MEMORY
  value: "3G"

Monitor real usage before increasing the heap:

kubectl top pod minecraft-0 \
  --namespace minecraft

Avoid assigning the entire Kubernetes node memory capacity to Minecraft. Kubernetes, the container runtime, DNS, networking components, and the node operating system require memory headroom.

StorageClass

The PVC currently uses:

storageClassName: local-path

Verify your cluster provides this StorageClass:

kubectl get storageclass

Change storageClassName in 01-minecraft-pvc.yaml when deploying to a cluster that uses a different storage provisioner.

Removing the Deployment

Remove the workloads and Services while retaining the PVC:

kubectl delete -f 05-playit-deployment.yaml
kubectl delete -f 03-minecraft-statefulset.yaml
kubectl delete -f 02-minecraft-services.yaml

Deleting the namespace will also delete namespace-scoped resources, including the PVC:

kubectl delete namespace minecraft

Do not delete the namespace unless you have backed up the Minecraft world or intentionally want to remove it.

License

This repository contains Kubernetes deployment manifests and documentation only.

Minecraft, Paper, Playit, Kubernetes, and the referenced container images remain subject to their respective licenses, terms, and usage policies.

About

Minecraft Kubernetes Homelab Deployment

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages