From 1cf47477d344f8f351bc821c12efffb7f53a205c Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Sun, 29 Mar 2026 18:17:42 -0500 Subject: [PATCH] Add MUDCraft Minecraft server infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Purpur 1.21.4 server via itzg/docker-minecraft-server (java21) - StatefulSet with 3 PVCs: worlds (20Gi), plugins (5Gi), logs (5Gi) - Aikar G1GC JVM flags, 6GB heap (sized for Legion's 15GB RAM) - Postgres: mudcraft database + user in existing platform/postgres - RCON secret + DB password via ExternalSecret → Vault - Traefik TCP entrypoint on port 25565 for mudcraft.nook.family - mudcraft namespace via Terraform - Argo CD app watching k8s/mudcraft/ --- servers/legion/apps/mudcraft.yaml | 20 +++++ servers/legion/k8s/mudcraft/configmap.yaml | 62 +++++++++++++ .../legion/k8s/mudcraft/external-secrets.yaml | 23 +++++ .../legion/k8s/mudcraft/ingressroutetcp.yaml | 15 ++++ servers/legion/k8s/mudcraft/pvc.yaml | 39 ++++++++ servers/legion/k8s/mudcraft/service.yaml | 32 +++++++ servers/legion/k8s/mudcraft/statefulset.yaml | 89 +++++++++++++++++++ servers/legion/namespaces.tf | 10 +++ servers/legion/traefik.tf | 6 ++ 9 files changed, 296 insertions(+) create mode 100644 servers/legion/apps/mudcraft.yaml create mode 100644 servers/legion/k8s/mudcraft/configmap.yaml create mode 100644 servers/legion/k8s/mudcraft/external-secrets.yaml create mode 100644 servers/legion/k8s/mudcraft/ingressroutetcp.yaml create mode 100644 servers/legion/k8s/mudcraft/pvc.yaml create mode 100644 servers/legion/k8s/mudcraft/service.yaml create mode 100644 servers/legion/k8s/mudcraft/statefulset.yaml diff --git a/servers/legion/apps/mudcraft.yaml b/servers/legion/apps/mudcraft.yaml new file mode 100644 index 0000000..85c28c4 --- /dev/null +++ b/servers/legion/apps/mudcraft.yaml @@ -0,0 +1,20 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: mudcraft + namespace: argocd +spec: + project: default + source: + repoURL: http://10.43.1.53:3000/will/infrastructure.git + targetRevision: main + path: servers/legion/k8s/mudcraft + destination: + server: https://kubernetes.default.svc + namespace: mudcraft + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/servers/legion/k8s/mudcraft/configmap.yaml b/servers/legion/k8s/mudcraft/configmap.yaml new file mode 100644 index 0000000..7ff3dee --- /dev/null +++ b/servers/legion/k8s/mudcraft/configmap.yaml @@ -0,0 +1,62 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mudcraft-config + namespace: mudcraft +data: + # itzg/docker-minecraft-server env-based config + # These are injected as env vars — the entrypoint generates server.properties + TYPE: "PURPUR" + VERSION: "1.21.4" + DIFFICULTY: "hard" + MAX_PLAYERS: "40" + VIEW_DISTANCE: "8" + SIMULATION_DISTANCE: "6" + ALLOW_FLIGHT: "true" + ONLINE_MODE: "true" + SPAWN_PROTECTION: "0" + MAX_TICK_TIME: "-1" + RATE_LIMIT: "0" + ENFORCE_WHITELIST: "true" + WHITELIST_ENABLED: "true" + ENABLE_RCON: "true" + RCON_PORT: "25575" + MOTD: "MUDCraft — Private Beta" + MEMORY: "" # Disabled — we set JVM flags directly + JVM_OPTS: >- + -Xms6G -Xmx6G + -XX:+UseG1GC + -XX:+ParallelRefProcEnabled + -XX:MaxGCPauseMillis=200 + -XX:+UnlockExperimentalVMOptions + -XX:+DisableExplicitGC + -XX:+AlwaysPreTouch + -XX:G1NewSizePercent=30 + -XX:G1MaxNewSizePercent=40 + -XX:G1HeapRegionSize=8M + -XX:G1ReservePercent=20 + -XX:G1HeapWastePercent=5 + -XX:G1MixedGCCountTarget=4 + -XX:InitiatingHeapOccupancyPercent=15 + -XX:G1MixedGCLiveThresholdPercent=90 + -XX:G1RSetUpdatingPauseTimePercent=5 + -XX:SurvivorRatio=32 + -XX:+PerfDisableSharedMem + -XX:MaxTenuringThreshold=1 + -XX:+UseStringDeduplication + -Djava.awt.headless=true + -Dfile.encoding=UTF-8 + -Dusing.aikars.flags=https://mcflags.emc.gs + -Daikars.new.flags=true + # Bukkit spawn limits — keep lower than default; MythicMobs adds custom mobs on top + OVERRIDE_BUKKIT_CONFIG: | + spawn-limits: + monsters: 40 + animals: 8 + water-animals: 3 + water-ambient: 8 + ambient: 4 + ticks-per: + monster-spawns: 2 + animal-spawns: 400 diff --git a/servers/legion/k8s/mudcraft/external-secrets.yaml b/servers/legion/k8s/mudcraft/external-secrets.yaml new file mode 100644 index 0000000..762d814 --- /dev/null +++ b/servers/legion/k8s/mudcraft/external-secrets.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: mudcraft-secrets + namespace: mudcraft +spec: + refreshInterval: 1h + secretStoreRef: + name: vault + kind: ClusterSecretStore + target: + name: mudcraft-secrets + creationPolicy: Owner + data: + - secretKey: db-password + remoteRef: + key: secret/data/legion-db + property: mudcraft_db_password + - secretKey: rcon-password + remoteRef: + key: secret/data/mudcraft + property: rcon_password diff --git a/servers/legion/k8s/mudcraft/ingressroutetcp.yaml b/servers/legion/k8s/mudcraft/ingressroutetcp.yaml new file mode 100644 index 0000000..f8870e7 --- /dev/null +++ b/servers/legion/k8s/mudcraft/ingressroutetcp.yaml @@ -0,0 +1,15 @@ +--- +# Traefik TCP route — exposes mudcraft.nook.family:25565 → mudcraft service +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: mudcraft-minecraft + namespace: mudcraft +spec: + entryPoints: + - minecraft + routes: + - match: HostSNI(`*`) + services: + - name: mudcraft + port: 25565 diff --git a/servers/legion/k8s/mudcraft/pvc.yaml b/servers/legion/k8s/mudcraft/pvc.yaml new file mode 100644 index 0000000..3c5da07 --- /dev/null +++ b/servers/legion/k8s/mudcraft/pvc.yaml @@ -0,0 +1,39 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mudcraft-worlds + namespace: mudcraft +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 20Gi +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mudcraft-plugins + namespace: mudcraft +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 5Gi +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mudcraft-logs + namespace: mudcraft +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 5Gi diff --git a/servers/legion/k8s/mudcraft/service.yaml b/servers/legion/k8s/mudcraft/service.yaml new file mode 100644 index 0000000..ef10223 --- /dev/null +++ b/servers/legion/k8s/mudcraft/service.yaml @@ -0,0 +1,32 @@ +--- +# ClusterIP — Traefik TCP route targets this +apiVersion: v1 +kind: Service +metadata: + name: mudcraft + namespace: mudcraft +spec: + selector: + app: mudcraft + ports: + - name: minecraft + port: 25565 + targetPort: 25565 + protocol: TCP + type: ClusterIP +--- +# RCON — internal only, for server management +apiVersion: v1 +kind: Service +metadata: + name: mudcraft-rcon + namespace: mudcraft +spec: + selector: + app: mudcraft + ports: + - name: rcon + port: 25575 + targetPort: 25575 + protocol: TCP + type: ClusterIP diff --git a/servers/legion/k8s/mudcraft/statefulset.yaml b/servers/legion/k8s/mudcraft/statefulset.yaml new file mode 100644 index 0000000..e922b8d --- /dev/null +++ b/servers/legion/k8s/mudcraft/statefulset.yaml @@ -0,0 +1,89 @@ +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mudcraft + namespace: mudcraft + labels: + app: mudcraft +spec: + replicas: 1 + serviceName: mudcraft + selector: + matchLabels: + app: mudcraft + template: + metadata: + labels: + app: mudcraft + spec: + terminationGracePeriodSeconds: 120 + containers: + - name: mudcraft + image: itzg/minecraft-server:java21 + imagePullPolicy: Always + ports: + - name: minecraft + containerPort: 25565 + protocol: TCP + - name: rcon + containerPort: 25575 + protocol: TCP + envFrom: + - configMapRef: + name: mudcraft-config + env: + - name: RCON_PASSWORD + valueFrom: + secretKeyRef: + name: mudcraft-secrets + key: rcon-password + - name: DB_URL + value: "jdbc:postgresql://postgres-postgresql.platform.svc.cluster.local:5432/mudcraft" + - name: DB_USER + value: "mudcraft" + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: mudcraft-secrets + key: db-password + volumeMounts: + - name: worlds + mountPath: /data/worlds + - name: plugins + mountPath: /data/plugins + - name: logs + mountPath: /data/logs + resources: + requests: + memory: 7Gi + cpu: 2000m + limits: + memory: 8Gi + cpu: 6000m + lifecycle: + preStop: + exec: + command: ["/bin/sh", "-c", "rcon-cli stop && sleep 15"] + readinessProbe: + tcpSocket: + port: 25565 + initialDelaySeconds: 60 + periodSeconds: 15 + failureThreshold: 10 + livenessProbe: + tcpSocket: + port: 25565 + initialDelaySeconds: 120 + periodSeconds: 30 + failureThreshold: 5 + volumes: + - name: worlds + persistentVolumeClaim: + claimName: mudcraft-worlds + - name: plugins + persistentVolumeClaim: + claimName: mudcraft-plugins + - name: logs + persistentVolumeClaim: + claimName: mudcraft-logs diff --git a/servers/legion/namespaces.tf b/servers/legion/namespaces.tf index ff6169b..487003f 100644 --- a/servers/legion/namespaces.tf +++ b/servers/legion/namespaces.tf @@ -27,3 +27,13 @@ resource "kubernetes_namespace" "external_secrets" { } } } + +resource "kubernetes_namespace" "mudcraft" { + metadata { + name = "mudcraft" + labels = { + managed-by = "terraform" + tier = "game" + } + } +} diff --git a/servers/legion/traefik.tf b/servers/legion/traefik.tf index 00ff03a..7fd9b03 100644 --- a/servers/legion/traefik.tf +++ b/servers/legion/traefik.tf @@ -19,6 +19,12 @@ resource "kubernetes_manifest" "traefik_config" { web: redirectTo: port: websecure + minecraft: + port: 25565 + expose: + default: true + exposedPort: 25565 + protocol: TCP YAML } }