DEV Community

Celso Nery
Celso Nery

Posted on

Minecraft Server - Part 3: Running on Kubernetes

🇧🇷 Versão em português

Minecraft Server - Part 3: Running on Kubernetes

In Part 2 of this series, we got the Minecraft server running in Docker containers, with persistence via volumes and automatic restart. In this article, will bringing the same server into a Kubernetes cluster, gaining managed storage via PersistentVolumeClaim, isolation via Namespace, and the flexibility to run on any available node in the cluster.

Minecraft Bedrock on Kubernetes

---
apiVersion: v1
kind: Namespace
metadata:
  name: minecraft-br
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: minecraft-bedrock-pvc
  namespace: minecraft-br
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minecraft-bedrock-server
  namespace: minecraft-br
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minecraft-bedrock
  template:
    metadata:
      labels:
        app: minecraft-bedrock
    spec:
      containers:
        - name: minecraft-bedrock
          image: itzg/minecraft-bedrock-server:latest
          ports:
            - containerPort: 19132
              protocol: UDP
          env:
            - name: EULA
              value: "TRUE"
            - name: GAMEMODE
              value: "survival"
            - name: MAX_PLAYERS
              value: "10"
            - name: MOTD
              value: "Bedrock Minecraft Server!"
            - name: OPS
              value: "your_username"
          volumeMounts:
            - mountPath: /data
              name: minecraft-bedrock-data
      volumes:
        - name: minecraft-bedrock-data
          persistentVolumeClaim:
            claimName: minecraft-bedrock-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: minecraft-bedrock-service
  namespace: minecraft-br
spec:
  selector:
    app: minecraft-bedrock
  ports:
    - name: bedrock-udp
      port: 19132
      targetPort: 19132
      protocol: UDP
  type: LoadBalancer
Enter fullscreen mode Exit fullscreen mode

Notice the structure, which brings together several concepts already covered throughout the series:

  • Namespace (minecraft-br): isolates the Minecraft Bedrock resources from the rest of the cluster;
  • PersistentVolumeClaim (10Gi): ensures the saved world survives pod restarts and recreations, without it, every new pod would start with an empty world;
  • Deployment: runs a single replica (replicas: 1), multiple replicas of the same Minecraft world don't work, since the game wasn't designed for multiple instances accessing the same save simultaneously;
  • LoadBalancer-type Service, on UDP port 19132, exposing the server outside the cluster.

On on-premise clusters, the LoadBalancer type usually doesn't provision an IP automatically (that's a native cloud provider feature), you need a controller like MetalLB for the Service to actually receive a usable external IP. Without it, the Service's external IP will stay <pending> indefinitely.

Minecraft Java on Kubernetes

apiVersion: v1
kind: Namespace
metadata:
  name: minecraft-java
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: minecraft-pvc
  namespace: minecraft-java
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minecraft-server
  namespace: minecraft-java
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minecraft
  template:
    metadata:
      labels:
        app: minecraft
    spec:
      containers:
        - name: minecraft
          image: itzg/minecraft-server:latest
          ports:
            - containerPort: 25565
          env:
            - name: EULA
              value: "TRUE"
            - name: MEMORY
              value: "1G"
            - name: VERSION
              value: "latest"
            - name: MAX_PLAYERS
              value: "10"
            - name: MOTD
              value: "Welcome to the Minecraft server"
            - name: ONLINE_MODE
              value: "TRUE"
            - name: WHITELIST
              value: "false"
            - name: OPS
              value: "your_username"
          volumeMounts:
            - mountPath: /data
              name: minecraft-data
      volumes:
        - name: minecraft-data
          persistentVolumeClaim:
            claimName: minecraft-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: minecraft-service
  namespace: minecraft-java
spec:
  selector:
    app: minecraft
  ports:
    - name: mc-tcp
      port: 25565
      targetPort: 25565
      protocol: TCP
  type: LoadBalancer
Enter fullscreen mode Exit fullscreen mode

Unlike Bedrock, here the containerPort and Service ports use TCP (the default, when protocol isn't specified on the container, but made explicit on the Service for clarity), consistent with Minecraft Java communicating over TCP, as we saw in Part 2.

Some environment fields specific to this image:

  • MEMORY: sets the memory limit passed to the JVM internally by the image (equivalent to the -Xmx flag seen in the bare metal installation);
  • VERSION: lets you pin a specific Minecraft version (for example, "1.20.4") instead of always using the latest;
  • ONLINE_MODE: when set to "TRUE", requires players to be authenticated with a valid Microsoft/Mojang account, only disable this if you know exactly why you need to (usually for servers with pirated clients, which carries security and legal implications).

Applying the manifests

kubectl apply -f minecraft-bedrock.yaml
kubectl apply -f minecraft-java.yaml
Enter fullscreen mode Exit fullscreen mode

Checking whether the pods came up correctly:

kubectl -n minecraft-br get pods

and

kubectl -n minecraft-java get pods
Enter fullscreen mode Exit fullscreen mode

Following the startup logs (the first start downloads the server files, which can take a few minutes):

kubectl -n minecraft-java logs -f deployment/minecraft-server

and 

kubectl -n minecraft-br logs -f deployment/minecraft-bedrock-server
Enter fullscreen mode Exit fullscreen mode

Series wrap-up

Throughout this series, we saw the same Minecraft server through three stages: manual bare metal installation, containerization with Docker, and finally orchestration on a Kubernetes cluster. Each stage solved a limitation of the previous one, from manual process management, through persistence and automatic restart, to managed storage and independence from a single physical host.

It's worth reinforcing an important point that applies to all three approaches: since this is a stateful game (the saved "world"), it's not possible to scale horizontally by running multiple replicas of the same server, unlike a stateless API, Minecraft needs a single, consistent instance accessing the data volume at a time.

Top comments (1)

Collapse
 
morphoices profile image
MORPHOICΞS.

The step-by-step approach makes a complex Kubernetes setup much easier to follow and actually reproduce. ~