Files
homelab/cluster/applications/minecraft/DEPLOYMENT-PLAN.md
Hermes Agent service account 56110d52bd feat(minecraft): deploy journey-into-imagination PaperMC server
- PaperMC 26.1.2 via itzg/minecraft-server:2026.7.0
- Namespace: minecraft, Service: journey-into-imagination
- TCP port 10182 (non-standard) via ingress-nginx tcp forwarding
- Pure Storage CSI PVC (pure-block, 50Gi) for world data
- World seed hardcoded: -5177989977648707969
- RCON password via ExternalSecret + 1Password Connect
- SleepMost v5.6.2 plugin for single-player sleep
- Whitelist off at launch, toggle-ready
- ExternalDNS annotations for internal Technitium record
- Manual steps: UniFi port forward WAN:10182→10.1.71.80:10182,
  Cloudflare A record + SRV for journey-into-imagination.mk-labs.cloud
2026-07-19 13:36:27 -05:00

9.9 KiB

Journey Into Imagination — Minecraft Server Deployment Plan

Application: PaperMC Minecraft Server
Namespace: minecraft
Deployment name: papermc
Service / DNS name: journey-into-imagination
ArgoCD path: cluster/applications/minecraft/
Reviewed: Pending Ryan approval


Version Pinning

Component Version
itzg/minecraft-server image 2026.7.0
PaperMC (Minecraft version) 1.21.4 (build 232 — last stable 1.21.x)
SleepMost plugin 5.6.2 (auto-downloaded at server start)

Note on PaperMC versioning: PaperMC migrated to a new version scheme (26.x) as of mid-2026. The task requested 1.21.x, so we pin VERSION=1.21.4. When you're ready to upgrade to the latest stable, change VERSION in deployment.yaml and configmap.yaml to 26.2 and bump the image tag to the latest itzg/minecraft-server release. Check plugin compatibility (SleepMost) before upgrading.


Storage

  • StorageClass: pure-block (confirmed live — kubectl get storageclass)
  • PVC size: 50Gi — expandable via Pure Storage CSI if the world grows
  • Mount: /data inside the container (worlds, plugins, configs all live here)

Networking Architecture

Internet (players)
    |
    | DNS: journey-into-imagination.mk-labs.cloud → WAN IP (Cloudflare A record)
    |
UniFi UDM Pro
    | Port Forward: WAN:25565 → 10.1.71.80:25565 (TCP)
    |
ingress-nginx LoadBalancer (10.1.71.80)
    | tcp-services ConfigMap: 25565 → minecraft/journey-into-imagination:25565
    |
journey-into-imagination Service (ClusterIP, minecraft namespace)
    |
papermc Pod (port 25565)

Prerequisites — Manual Steps Before Deploying

1. Generate a real RCON password

# Generate a secure password
openssl rand -base64 24
# Output example: abc123... (save this — you'll need it)

# Base64-encode it for the Secret
echo -n 'YOUR_GENERATED_PASSWORD' | base64

Edit cluster/applications/minecraft/secret.yaml and replace the placeholder value:

data:
  rcon-password: <your-base64-encoded-password>

⚠️ Do NOT commit real credentials in plaintext. If this is a concern long-term, migrate to ExternalSecrets + 1Password (Day 2 operation).

2. UniFi Port Forward (Ryan — manual step)

In UniFi Network → Firewall & Security → Port Forwarding:

  • Name: minecraft-papermc
  • Protocol: TCP
  • External Port: 25565
  • Internal IP: 10.1.71.80 (ingress-nginx LoadBalancer)
  • Internal Port: 25565
  • Enabled: Yes

3. Cloudflare DNS Records (Ryan — manual step)

In Cloudflare Dashboard → mk-labs.cloud zone:

A Record (public server address):

Type Name Value Proxy
A journey-into-imagination <your-WAN-IP> DNS only (gray cloud)

Minecraft uses raw TCP — Cloudflare proxy (orange cloud) will NOT work. Use DNS-only mode (gray cloud).

SRV Record (allows clients to connect without specifying port):

Type Name Service Proto Priority Weight Port Target
SRV _minecraft._tcp.journey-into-imagination _minecraft _tcp 0 5 25565 journey-into-imagination.mk-labs.cloud

The SRV record allows players to connect using journey-into-imagination.mk-labs.cloud without specifying :25565. Most modern Minecraft clients resolve SRV records.

4. Internal DNS (Technitium — automated via ExternalDNS)

The Service in service.yaml has this annotation:

external-dns.alpha.kubernetes.io/hostname: journey-into-imagination.local.mk-labs.cloud

ExternalDNS (Technitium provider) will create the internal A record automatically when ArgoCD syncs. No manual action required for internal DNS.


Deployment Steps

Step 1: Update the RCON Secret

Complete prerequisite #1 above, then commit the updated secret.

Step 2: Commit and Push to Gitea

cd ~/git/homelab
git status   # review what changed
git add cluster/applications/minecraft/ cluster/platform/ingress-nginx/values.yaml
git commit -m "feat(minecraft): add Journey Into Imagination PaperMC server

- Namespace, Deployment, Service (journey-into-imagination), PVC, Secret, ConfigMap
- ArgoCD Application at cluster/applications/minecraft/
- ingress-nginx TCP forwarding: 25565 -> minecraft/journey-into-imagination:25565
- PaperMC 1.21.4, itzg/minecraft-server:2026.7.0, SleepMost 5.6.2
- StorageClass: pure-block, 50Gi PVC for world data"

git push

Step 3: Verify ArgoCD Discovers and Syncs

ArgoCD app-of-apps auto-discovers cluster/applications/minecraft/application.yaml.

# Watch ArgoCD pick it up (from carousel-of-progress)
kubectl get application minecraft -n argocd -w

# Or check ArgoCD UI at argocd.local.mk-labs.cloud

Wait for status: Synced / Healthy

Step 4: Watch the Pod Start Up

First startup will download the PaperMC jar — this can take 2-3 minutes.

kubectl get pods -n minecraft -w

# Tail the logs to watch PaperMC boot
kubectl logs -n minecraft -l app.kubernetes.io/component=papermc -f

Look for:

[Server thread/INFO]: Done (X.XXXs)! For help, type "help"

Step 5: Install SleepMost Plugin (auto via PLUGINS env var)

The PLUGINS env var in the ConfigMap points to the SleepMost JAR download URL. itzg/minecraft-server downloads and installs it automatically on startup.

Verify it loaded:

kubectl exec -n minecraft -it deployment/papermc -- rcon-cli
# In rcon console:
plugins
# Should list: SleepMost

Step 6: Verify Connectivity

Internal test (from carousel-of-progress):

# Port probe — should succeed
nc -zv journey-into-imagination.local.mk-labs.cloud 25565

# Or via the ClusterIP directly
kubectl get svc -n minecraft
nc -zv <CLUSTER-IP> 25565

External test (after UniFi port forward + DNS configured):

  • Open Minecraft Java Edition
  • Add server: journey-into-imagination.mk-labs.cloud
  • Should connect and show MOTD: "Journey Into Imagination"

Plugin Details

SleepMost v5.6.2

Default behavior: configurable percentage of online players must sleep to skip night. To tune (exec into the pod after first start):

kubectl exec -n minecraft -it deployment/papermc -- bash
cat /data/plugins/SleepMost/config.yml
# Edit as needed, then /sleepmost reload in rcon

Geyser compatibility note: SleepMost 5.x does NOT require Geyser to function. If you add Geyser later for Bedrock crossplay, SleepMost is compatible.


Day 2 Operations

Enabling the Whitelist

When ready to lock the server to approved players:

  1. Edit cluster/applications/minecraft/configmap.yaml:

    WHITE_LIST: "true"
    ENFORCE_WHITELIST: "true"
    
  2. Add players via RCON (live, no restart needed):

    kubectl exec -n minecraft -it deployment/papermc -- rcon-cli
    whitelist add <player_name>
    whitelist list
    
  3. Commit the ConfigMap change for GitOps consistency. Note: the Deployment will restart when the ConfigMap changes (env var reload). Players will be briefly disconnected — plan accordingly.

Console / RCON Access

# Interactive RCON (from inside the pod)
kubectl exec -n minecraft -it deployment/papermc -- rcon-cli

# One-shot command
kubectl exec -n minecraft -it deployment/papermc -- rcon-cli "list"
kubectl exec -n minecraft -it deployment/papermc -- rcon-cli "say Server restarting in 5 minutes"

Upgrading PaperMC Version

  1. Check PaperMC API: curl -sA 'Mozilla/5.0' https://fill.papermc.io/v3/projects/paper/versions | head
  2. Update VERSION in deployment.yaml env vars and the comment header
  3. Update image tag if a new itzg/minecraft-server release is out
  4. Verify SleepMost compatibility with the new version
  5. Commit, push — ArgoCD handles the rolling restart (Recreate strategy)

Expanding PVC Storage

Pure Storage CSI supports online volume expansion:

kubectl patch pvc papermc-world-data -n minecraft \
  -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'

No pod restart required.

Backups

No automated backup is configured in this skeleton. Recommended Day 2 addition: CronJob that runs rcon-cli save-all + rcon-cli save-off, copies /data/world* to a separate PVC or object store, then rcon-cli save-on.


Troubleshooting

Symptom Check
Pod stuck in Init / slow start kubectl logs -n minecraft -l app.kubernetes.io/component=papermc — jar download may be slow
Port 25565 connection refused externally Verify UniFi port forward is active; kubectl get svc -n minecraft shows correct ClusterIP
Players can't authenticate ONLINE_MODE=true requires Mojang auth; check player has a valid Java account
World data lost after pod restart Verify PVC is Bound; check pure-block StorageClass is healthy
EULA error in logs EULA=TRUE is set in deployment.yaml — this should not occur; check env var injection

File Manifest Summary

File Purpose
namespace.yaml minecraft namespace
application.yaml ArgoCD Application (sync-wave 20)
pvc.yaml 50Gi pure-block PVC for world data
secret.yaml RCON password (⚠️ placeholder — update before deploy)
configmap.yaml server.properties overrides + SleepMost plugin download
deployment.yaml PaperMC Deployment (Recreate strategy, resource limits set)
service.yaml ClusterIP Service named journey-into-imagination
../../../platform/ingress-nginx/values.yaml Added tcp: 25565 forwarding entry