Homelab
Minecraft Paper Server
Deploy a Minecraft Paper Server on Kubernetes with Persistent Storage and Tailscale Access
Overview
This article walks through deploying a Minecraft Java Edition server on your Kubernetes cluster with persistent world storage and Tailscale-based remote access. You will use the itzg/minecraft-server Helm chart with Paper server type for optimal performance and plugin support.
| Tip: | Having trouble? See v0.12.0 for what your setup should look like after completing this article. |
Before You Begin
Prerequisites
- MetalLB, Longhorn, and Ingress-NGINX completed
Create Minecraft Manifests
Using the itzg/minecraft-server chart1 with the Docker Minecraft Server image2. Server types include VANILLA, PAPER3, FORGE, and FABRIC - we use PAPER for performance optimizations and plugin support. The configuration includes Aikar's JVM flags4 for optimal Paper performance.
Init Workspace
cd ~/homelab
export KUBECONFIG=$(pwd)/talos/clusterconfig/kubeconfig
mkdir -p k8s/apps/minecraft
git checkout -b dev Namespace
k8s/apps/minecraft/namespace.yaml:
---
apiVersion: v1
kind: Namespace
metadata:
name: minecraft PersistentVolumeClaim
Persistent storage for world data.
k8s/apps/minecraft/pvc.yaml:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minecraft-data
namespace: minecraft
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 20Gi HelmRelease
k8s/apps/minecraft/helmrelease.yaml:
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: minecraft
namespace: minecraft
spec:
interval: 24h
url: https://itzg.github.io/minecraft-server-charts
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: minecraft
namespace: minecraft
spec:
interval: 30m
chart:
spec:
chart: minecraft
sourceRef:
kind: HelmRepository
name: minecraft
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
# Server type options: VANILLA, PAPER, FORGE, FABRIC
# We use PAPER for performance and plugin support
minecraftServer:
eula: 'TRUE'
version: '1.21.11'
type: 'PAPER'
difficulty: normal
motd: 'Experience is merely the name men gave to their mistakes.'
maxPlayers: 10
gameMode: survival
pvp: false
onlineMode: true # Requires Mojang auth
memory: 4096M
viewDistance: 32
enableCommandBlock: true
serviceType: LoadBalancer # MetalLB assigns IP for Tailscale access
# Persistence - use our PVC for world data
persistence:
dataDir:
enabled: true
existingClaim: minecraft-data
# Resource limits - scale based on player count:
# 1-5 players: 2-3GB RAM, 1-2 cores
# 5-10 players: 3-4GB RAM, 2-3 cores
# 10-20 players: 4-6GB RAM, 3-4 cores
resources:
requests:
memory: 4Gi
cpu: 2000m
limits:
memory: 6Gi
cpu: 4000m
# Aikar's JVM flags for optimal Paper performance
extraEnv:
JVM_XX_OPTS: '-XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+ParallelRefProcEnabled -XX:+PerfDisableSharedMem -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1HeapRegionSize=8M -XX:G1HeapWastePercent=5 -XX:G1MaxNewSizePercent=40 -XX:G1MixedGCCountTarget=4 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1NewSizePercent=30 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:G1ReservePercent=20 -XX:InitiatingHeapOccupancyPercent=15 -XX:MaxGCPauseMillis=200 -XX:MaxTenuringThreshold=1 -XX:SurvivorRatio=32 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true' Kustomization
k8s/apps/minecraft/kustomization.yaml:
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- pvc.yaml
- helmrelease.yaml Apps Kustomization
Add minecraft to k8s/apps/kustomization.yaml:
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- plex
- factorio
- minecraft Deploy Minecraft
Commit Changes
cd ~/homelab
git add k8s/apps/minecraft/ k8s/apps/kustomization.yaml
git commit -m "feat(minecraft): add Minecraft server"
git checkout main
git merge --ff-only dev
git push Reconcile Flux
flux reconcile source git flux-system && flux reconcile kustomization sync Verify Minecraft
Deployment Status
# Check HelmRelease status
flux get helmreleases -n minecraft
# Check pods (first startup takes 2-5 min)
kubectl get pods -n minecraft -w
# Check service (note the EXTERNAL-IP)
kubectl get svc -n minecraft
# View logs
kubectl logs -n minecraft -l app=minecraft -f Tailscale Access
Get the LoadBalancer IP:
kubectl get svc -n minecraft minecraft -o jsonpath='{.status.loadBalancer.ingress[0].ip}' Connect:
- Ensure your device is connected to Tailscale
- Open Minecraft Java Edition
- Multiplayer → Add Server
- Server Address:
<EXTERNAL-IP>:25565 - Save and connect
At this point, Minecraft is working via Tailscale.
Next Steps
This concludes the core homelab series. Your cluster now runs media streaming, game servers, and has secure remote access from anywhere.
For optional enhancements to your Minecraft server:
- Add playit.gg for public access (friends without Tailscale)
- Import an existing world
- Enable Bedrock support
- Server management commands
Resources
Footnotes
itzg, "Minecraft Server Charts," github.com. Accessed: Dec. 22, 2025. [Online]. Available: https://github.com/itzg/minecraft-server-charts ↩
itzg, "Docker Minecraft Server," github.com. Accessed: Dec. 22, 2025. [Online]. Available: https://github.com/itzg/docker-minecraft-server ↩
PaperMC, "Paper Documentation," docs.papermc.io. Accessed: Dec. 22, 2025. [Online]. Available: https://docs.papermc.io/ ↩
PaperMC, "Aikar's Flags," docs.papermc.io. Accessed: Dec. 22, 2025. [Online]. Available: https://docs.papermc.io/paper/aikars-flags ↩