---
title: Notes on Installing Minecraft Server (Java Edition) on Kubernetes
tags: ["Minecraft", "Java", "Kubernetes"]
categories: ["Game", "Minecraft"]
date: 2025-01-03T08:08:50Z
updated: 2025-01-03T17:28:55Z
---

> ⚠️ This article was automatically translated by OpenAI (gpt-4o-mini).
> It may be edited eventually, but please be aware that it may contain incorrect information at this time.

Minecraft Server has a feature-rich [helm chart](https://github.com/itzg/minecraft-server-charts/tree/master), making it easy to operate when installed on Kubernetes.

In this article, I will document the installation of Minecraft Server on Kind (Kubernetes in Docker) as a testing environment.

### Creating a Kind Cluster

```bash
brew install kind
kind create cluster
```

```bash
$ kubectl get node -owide
NAME                 STATUS   ROLES           AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION                        CONTAINER-RUNTIME
kind-control-plane   Ready    control-plane   39s   v1.32.0   192.168.107.2   <none>        Debian GNU/Linux 12 (bookworm)   6.12.5-orbstack-00287-gf8da5d508983   containerd://1.7.24
```

### Installing MetalLB

I am using [OrbStack](https://orbstack.dev/) as the Docker Runtime. With OrbStack, routing from the Mac host to a Service of type=LoadBalancer is possible. We will install MetalLB to ensure that an External IP is assigned to a Service of type=LoadBalancer on Kind.

```bash
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
kubectl wait --namespace metallb-system \
             --for=condition=ready pod \
             --selector=app=metallb \
             --timeout=90s
```

Next, we will obtain the IPv4 Subnet of the Docker Network.

```bash
SUBNET=$(docker network inspect -f '{{(index (index .IPAM.Config 1) "Subnet")}}' kind)
```

Let's check it.

```bash
$ echo $SUBNET
192.168.107.0/24
```

> [!TIP]
> If you cannot obtain it, try `SUBNET=$(docker network inspect -f '{{(index (index .IPAM.Config 0) "Subnet")}}' kind)`.

Now, we will set the range of IPs that MetalLB will assign. In this example, we will target `192.168.107.200-192.168.107.250`.

```yaml
cat <<EOF > metallb.yaml
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: example
  namespace: metallb-system
spec:
  addresses:
  - $(echo $SUBNET | cut -d. -f1-3).200-$(echo $SUBNET | cut -d. -f1-3).250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: empty
  namespace: metallb-system
---
EOF
kubectl apply -f metallb.yaml
```

### Installing Minecraft Server

With the preparations complete, we can finally install the Minecraft Server.

We will use the helm chart from https://github.com/itzg/minecraft-server-charts/tree/master/charts/minecraft.

The Server's IP will be fixed to the following IP.

```yaml
MINECRAFT_IP=$(echo $SUBNET | cut -d. -f1-3).222
```

```bash
$ echo $MINECRAFT_IP 
192.168.107.222
```

Create the `helm-values.yaml` file. Here, we will install the Vanilla Server, but you can choose `"VANILLA"`, `"FORGE"`, `"PAPER"`, etc. for the `type`. For available types, please refer to [this link](https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/).

```yaml
cat <<EOF > helm-values.yaml
---
minecraftServer:
  eula: "TRUE"
  type: "VANILLA"
  version: "1.21.4"
  difficulty: easy
  gameMode: creative
  overrideServerProperties: true
  serviceType: LoadBalancer
  loadBalancerIP: $MINECRAFT_IP
  externalTrafficPolicy: Local
  memory: 4096M
  rcon:
    enabled: true
resources:
  requests:
    memory: 3.5Gi
persistence:
  dataDir:
    enabled: true
    Size: 8Gi
---
EOF
```

> [!TIP] 
> I am operating on Oracle Cloud Infrastructure Container Engine for Kubernetes (OKE).
> For the `helm-values.yaml` on OKE, please refer to [this link](https://github.com/making/k8s-gitops/blob/main/lemon/app/minecraft/helm-values.yaml).

Now, let's install the Minecraft Server using helm.

```bash
helm upgrade --install \
  -n minecraft minecraft itzg/minecraft \
  -f helm-values.yaml \
  --create-namespace \
  --wait
```

After a while, the Pod will be Ready, and the LoadBalancer will be created with the specified External IP.

```bash
$ kubectl get pod,svc -n minecraft 
NAME                                       READY   STATUS    RESTARTS   AGE
pod/minecraft-minecraft-58746c5689-lcwxw   1/1     Running   0          66s

NAME                               TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)           AGE
service/minecraft-minecraft        LoadBalancer   10.96.5.89      192.168.107.222   25565:32239/TCP   66s
service/minecraft-minecraft-rcon   ClusterIP      10.96.218.111   <none>            25575/TCP         66s
```

You can also check the status with the helm command.

```bash
$ helm list -A
NAME     	NAMESPACE	REVISION	UPDATED                            	STATUS  	CHART           	APP VERSION
minecraft	minecraft	1       	2025-01-03 16:47:39.39652 +0900 JST	deployed	minecraft-4.23.3	SeeValues  
```

Now, connect to this Server from the client.

<img width="966" src="https://github.com/user-attachments/assets/fb168f78-b545-487e-bdd2-3b34815777e3" />

Successfully connected.

<img width="966" src="https://github.com/user-attachments/assets/84adb9cd-8e64-493d-bd36-df116fda214d" />

You can uninstall it with the following command. If you are on kind, you can experiment freely.

```bash
helm uninstall -n minecraft minecraft --wait
```
