Skip to main content
Kubernetes provides robust orchestration capabilities for running Minecraft servers in production environments with high availability, auto-scaling, and resource management.

Deployment Methods

Helm Charts

Simplified deployment using community Helm charts

StatefulSets

Direct deployment with Kubernetes manifests

Operators

Automated management with Shulker operator

Simple Deployment

Basic Deployment with emptyDir storage

Using Helm

itzg Helm Chart

The official Helm chart provides the easiest deployment method:
1

Add the Helm repository

helm repo add itzg https://itzg.github.io/minecraft-server-charts/
helm repo update
2

Install the chart

helm install my-minecraft itzg/minecraft \
  --set minecraftServer.eula=true \
  --set persistence.dataDir.enabled=true
3

Verify deployment

kubectl get pods
kubectl get svc

Custom Values

Create a values.yaml file for customization:
values.yaml
minecraftServer:
  eula: true
  version: "1.21.4"
  type: PAPER
  difficulty: normal
  maxPlayers: 50
  viewDistance: 10
  memory: 4096M
  serviceType: LoadBalancer
  
persistence:
  dataDir:
    enabled: true
    size: 10Gi
    storageClass: "standard"

resources:
  requests:
    memory: 4Gi
    cpu: 2000m
  limits:
    memory: 6Gi
    cpu: 4000m
Then install with:
helm install my-minecraft itzg/minecraft -f values.yaml
View the full chart documentation at itzg/minecraft-server-charts

mcsh/server-deployment

Alternative Helm chart with additional features:
helm repo add mcsh https://mcserverhosting-net.github.io/charts/
helm install minecraft mcsh/minecraft

Using StatefulSets

StatefulSets are ideal for Minecraft servers as they provide:
  • Stable network identities
  • Persistent storage
  • Ordered deployment and scaling
  • Stable persistent volume claims

Complete StatefulSet Example

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: mc-example
  name: mc-example
spec:
  replicas: 1
  serviceName: mc-example
  selector:
    matchLabels:
      app: mc-example
  template:
    metadata:
      labels:
        app: mc-example
    spec:
      containers:
        - name: mc
          image: itzg/minecraft-server
          imagePullPolicy: Always
          env:
            - name: EULA
              value: "TRUE"
            - name: TYPE
              value: PAPER
            - name: VERSION
              value: "1.21.4"
            - name: MEMORY
              value: "4G"
          ports:
            - containerPort: 25565
              name: minecraft
              protocol: TCP
          volumeMounts:
            - mountPath: /data
              name: data
          readinessProbe:
            exec:
              command:
                - mc-monitor
                - status
                - --host
                - localhost
                - --port
                - "25565"
            initialDelaySeconds: 30
            periodSeconds: 5
            failureThreshold: 18
          livenessProbe:
            exec:
              command:
                - mc-monitor
                - status
                - --host
                - localhost
            initialDelaySeconds: 120
            periodSeconds: 60
          resources:
            requests:
              memory: "4Gi"
              cpu: "2000m"
            limits:
              memory: "6Gi"
              cpu: "4000m"
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 10Gi
        storageClassName: standard
---
apiVersion: v1
kind: Service
metadata:
  labels:
    service: mc-example
  name: mc-example
spec:
  ports:
    - port: 25565
      targetPort: 25565
      protocol: TCP
  selector:
    app: mc-example
  type: LoadBalancer
1

Apply the manifest

kubectl apply -f statefulset.yaml
2

Check status

kubectl get statefulset mc-example
kubectl get pods -l app=mc-example
kubectl get pvc
3

View logs

kubectl logs -f mc-example-0
4

Get service endpoint

kubectl get svc mc-example

Key Configuration Elements

Readiness Probe - Determines when the pod is ready to accept traffic:
readinessProbe:
  exec:
    command:
      - mc-monitor
      - status
      - --host
      - localhost
      - --port
      - "25565"
  initialDelaySeconds: 30
  periodSeconds: 5
  failureThreshold: 18
This gives the server up to 120 seconds to start (30 + 5 × 18).Liveness Probe - Detects if the server has become unresponsive:
livenessProbe:
  exec:
    command:
      - mc-monitor
      - status
      - --host
      - localhost
  initialDelaySeconds: 120
  periodSeconds: 60
Set initialDelaySeconds high enough for server startup to avoid premature restarts.
Define CPU and memory resources:
resources:
  requests:
    memory: "4Gi"    # Guaranteed allocation
    cpu: "2000m"     # 2 cores
  limits:
    memory: "6Gi"    # Maximum allowed
    cpu: "4000m"     # 4 cores max
Guidelines:
  • Requests: What the pod needs to run
  • Limits: Maximum the pod can use
  • Set memory limits ~1.5x higher than JVM heap
  • CPU limits prevent resource starvation
Volume claim templates create persistent storage:
volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi
      storageClassName: standard
Storage classes vary by provider:
  • GKE: standard, standard-rwo
  • EKS: gp2, gp3
  • AKS: default, managed-premium
PVCs created by StatefulSets are not deleted when the StatefulSet is deleted.
LoadBalancer (recommended for cloud providers):
spec:
  type: LoadBalancer
  ports:
    - port: 25565
      targetPort: 25565
NodePort (for on-premises or manual load balancing):
spec:
  type: NodePort
  ports:
    - port: 25565
      nodePort: 30000
ClusterIP with Ingress (for TCP proxy):
spec:
  type: ClusterIP
  ports:
    - port: 25565

Simple Deployment

For testing or development, use a basic Deployment:
deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: mc-vanilla
spec:
  type: NodePort
  ports:
  - port: 25565
    nodePort: 30000
  selector:
    app: mc-vanilla
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mc-vanilla
spec:
  selector:
    matchLabels:
      app: mc-vanilla
  template:
    metadata:
      labels:
        app: mc-vanilla
    spec:
      containers:
      - image: itzg/minecraft-server
        name: mc-vanilla
        env:
        - name: EULA
          value: "true"
        ports:
        - containerPort: 25565
          name: main
        readinessProbe:
          exec:
            command: ["/usr/local/bin/mc-monitor", "status", "--host", "localhost"]
          initialDelaySeconds: 20
          periodSeconds: 5
          failureThreshold: 20
        livenessProbe:
          exec:
            command: ["/usr/local/bin/mc-monitor", "status", "--host", "localhost"]
          initialDelaySeconds: 120
          periodSeconds: 60
        volumeMounts:
        - name: mc-data
          mountPath: /data
      volumes:
      - name: mc-data
        emptyDir: {}
This uses emptyDir which loses data when the pod is deleted. Only use for testing!

Using Operators

Shulker Operator

Shulker is a Kubernetes operator for managing complex Minecraft infrastructures:
1

Install the operator

kubectl apply -f https://github.com/jeremylvln/Shulker/releases/latest/download/shulker.yaml
2

Create a server

minecraft-server.yaml
apiVersion: shulker.io/v1alpha1
kind: MinecraftServer
metadata:
  name: my-server
spec:
  version: "1.21.4"
  type: PAPER
  replicas: 1
  memory: "4Gi"
  storage:
    size: 10Gi
3

Apply the configuration

kubectl apply -f minecraft-server.yaml
Shulker uses itzg/minecraft-server under the hood and provides CRDs for easier management.

Configuration Management

Using ConfigMaps

Store server.properties in a ConfigMap:
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mc-server-properties
data:
  server.properties: |
    motd=My Kubernetes Server
    max-players=50
    view-distance=10
    difficulty=normal
---
apiVersion: apps/v1
kind: StatefulSet
spec:
  template:
    spec:
      containers:
      - name: mc
        volumeMounts:
        - name: config
          mountPath: /data/server.properties
          subPath: server.properties
      volumes:
      - name: config
        configMap:
          name: mc-server-properties

Using Secrets

Store sensitive data like RCON passwords:
kubectl create secret generic mc-rcon \
  --from-literal=password='your-secure-password'
env:
  - name: RCON_PASSWORD
    valueFrom:
      secretKeyRef:
        name: mc-rcon
        key: password

Scaling Considerations

Minecraft servers are stateful and cannot be horizontally scaled. Keep replicas at 1.

Vertical Scaling

Increase resources for a single server:
kubectl patch statefulset mc-example -p '{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "mc",
          "resources": {
            "requests": {"memory": "8Gi", "cpu": "4000m"},
            "limits": {"memory": "12Gi", "cpu": "6000m"}
          }
        }]
      }
    }
  }
}'

Multiple Servers

Deploy separate StatefulSets for multiple servers:
# Survival server
helm install survival itzg/minecraft -f survival-values.yaml

# Creative server
helm install creative itzg/minecraft -f creative-values.yaml

Monitoring and Debugging

View Logs

# Follow logs
kubectl logs -f mc-example-0

# Last 100 lines
kubectl logs --tail=100 mc-example-0

# Previous instance
kubectl logs --previous mc-example-0

Execute Commands

# Attach to console
kubectl attach mc-example-0 -it

# Execute RCON command
kubectl exec mc-example-0 -- rcon-cli list

# Access shell
kubectl exec -it mc-example-0 -- /bin/bash

Check Status

# Pod status
kubectl describe pod mc-example-0

# Server status
kubectl exec mc-example-0 -- mc-monitor status

# Resource usage
kubectl top pod mc-example-0

Backup and Restore

Manual Backup

# Create backup
kubectl exec mc-example-0 -- tar czf /tmp/backup.tar.gz /data
kubectl cp mc-example-0:/tmp/backup.tar.gz ./backup.tar.gz

Automated Backups

Use a CronJob for regular backups:
backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: mc-backup
spec:
  schedule: "0 2 * * *"  # 2 AM daily
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: itzg/mc-backup
            env:
            - name: BACKUP_INTERVAL
              value: "24h"
            volumeMounts:
            - name: data
              mountPath: /data
              readOnly: true
            - name: backups
              mountPath: /backups
          volumes:
          - name: data
            persistentVolumeClaim:
              claimName: data-mc-example-0
          - name: backups
            persistentVolumeClaim:
              claimName: mc-backups
          restartPolicy: OnFailure

Cloud Provider Examples

Google Kubernetes Engine (GKE)

gke-values.yaml
persistence:
  dataDir:
    storageClass: "standard-rwo"
    size: 20Gi

resources:
  requests:
    memory: 4Gi
    cpu: 2
  limits:
    memory: 6Gi
    cpu: 4

minecraftServer:
  serviceType: LoadBalancer
  serviceAnnotations:
    cloud.google.com/load-balancer-type: "Internal"

Amazon EKS

eks-values.yaml
persistence:
  dataDir:
    storageClass: "gp3"
    size: 20Gi

minecraftServer:
  serviceType: LoadBalancer
  serviceAnnotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

Azure AKS

aks-values.yaml
persistence:
  dataDir:
    storageClass: "managed-premium"
    size: 20Gi

minecraftServer:
  serviceType: LoadBalancer
  serviceAnnotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"

Next Steps

Docker Swarm

Deploy on Docker Swarm clusters

More Examples

Explore additional deployment patterns

Additional Resources

Build docs developers (and LLMs) love