PaperMCServer¶
The PaperMCServer CRD defines a Minecraft server instance managed by the operator.
Overview¶
apiVersion: mc.k8s.lex.la/v1beta1
kind: PaperMCServer
metadata:
name: my-server
namespace: minecraft
spec:
updateStrategy: "auto"
# ... configuration
Spec Fields¶
updateStrategy¶
Required — Defines how Paper version updates are handled.
| Value | Description |
|---|---|
latest | Always use newest Paper version from Docker Hub (ignores plugin compatibility) |
auto | Constraint solver picks best version compatible with all plugins |
pin | Stay on specific version, auto-update to latest build |
build-pin | Fully pinned version and build, no automatic updates |
See Update Strategies for detailed guide.
version¶
Optional — Target Paper version. Required for pin and build-pin strategies.
build¶
Optional — Target Paper build number.
- With
pinstrategy: minimum build (operator will still auto-update to newer builds) - With
build-pinstrategy: exact build (no automatic updates)
updateDelay¶
Optional — Grace period before applying Paper updates. Useful for waiting for community feedback on new releases.
updateSchedule¶
Required — Defines when to check and apply updates.
| Field | Description |
|---|---|
checkCron | Cron expression for checking updates |
maintenanceWindow.enabled | Enable scheduled updates |
maintenanceWindow.cron | Cron expression for applying updates |
spec:
updateSchedule:
checkCron: "0 3 * * *" # Check daily at 3am
maintenanceWindow:
enabled: true
cron: "0 4 * * 0" # Apply updates Sunday 4am
Cron Format
Standard cron format: minute hour day-of-month month day-of-week
gracefulShutdown¶
Required — Configures graceful server shutdown.
| Field | Description |
|---|---|
timeout | Shutdown timeout (should match terminationGracePeriodSeconds) |
Timeout Configuration
The timeout should match your StatefulSet's terminationGracePeriodSeconds. Too short may cause world corruption. Minimum recommended: 300s.
rcon¶
Required — Configures RCON for graceful shutdown commands.
| Field | Description | Default |
|---|---|---|
enabled | Enable RCON | — |
passwordSecret.name | Secret name | — |
passwordSecret.key | Key in Secret | — |
port | RCON port | 25575 |
Create the secret:
service¶
Optional — Configures the Kubernetes Service for the server.
| Field | Description | Default |
|---|---|---|
type | Service type | LoadBalancer |
annotations | Custom annotations | — |
loadBalancerIP | Static IP for LoadBalancer | — |
spec:
service:
type: LoadBalancer
annotations:
metallb.universe.tf/loadBalancerIPs: "192.168.1.100"
loadBalancerIP: "192.168.1.100"
network¶
Optional — Configures a Kubernetes NetworkPolicy for the server.
| Field | Description | Default |
|---|---|---|
networkPolicy.enabled | Create a NetworkPolicy for this server | — |
networkPolicy.allowFrom | Additional ingress sources for the Minecraft port | — |
networkPolicy.restrictEgress | Restrict outbound traffic to DNS and HTTPS only | true |
networkPolicy.allowEgressTo | Additional egress destinations when restrictEgress is true | — |
spec:
network:
networkPolicy:
enabled: true
allowFrom:
- cidr: "10.0.0.0/8"
- podSelector:
matchLabels:
role: proxy
restrictEgress: true
allowEgressTo:
- cidr: "203.0.113.0/24"
port: 8080
protocol: TCP
When enabled, the NetworkPolicy allows ingress on the Minecraft game port (25565) and RCON port (if configured), plus any sources listed in allowFrom. Egress is restricted to DNS (port 53) and HTTPS (port 443) by default; use allowEgressTo for additional destinations. Matched Plugin ports are automatically added as ingress rules.
gateway¶
Optional — Configures Gateway API TCPRoute/UDPRoute for game traffic routing. Requires Gateway API CRDs (experimental channel) installed in the cluster.
| Field | Description | Default |
|---|---|---|
enabled | Create Gateway API routes for this server | — |
parentRefs | Gateway(s) that routes should attach to | — |
tcpRoute.enabled | Create a TCPRoute for game traffic | — |
udpRoute.enabled | Create a UDPRoute for game traffic | — |
httpRoutes | HTTPRoute configurations for plugin HTTP endpoints | — |
httpRoutes[].pluginName | Name of the Plugin resource (same namespace) | — |
httpRoutes[].endpointName | Name of the HTTP endpoint in the plugin | — |
httpRoutes[].hostname | FQDN for this HTTPRoute | — |
httpRoutes[].pathPrefix | Optional path prefix (e.g., /map) | — |
spec:
gateway:
enabled: true
parentRefs:
- name: minecraft-gateway
namespace: gateway-system
sectionName: minecraft-tcp
tcpRoute:
enabled: true
udpRoute:
enabled: true
# HTTPRoutes for plugin web interfaces
httpRoutes:
- pluginName: bluemap
endpointName: web-ui
hostname: map.minecraft.example.com
- pluginName: plan
endpointName: web-ui
hostname: analytics.minecraft.example.com
pathPrefix: /plan
The operator creates TCPRoute and/or UDPRoute resources that route the Minecraft game port (25565) through the specified Gateway. HTTPRoute resources are created for each httpRoutes entry that references a matched Plugin with an HTTP-protocol endpoint. If the Gateway API CRDs are not installed in the cluster, the operator gracefully skips route management (logs a debug message, no error). When Gateway API CRDs are installed, external modifications to the routes are automatically detected and corrected via owner-reference watches.
backup¶
Optional — Configures VolumeSnapshot-based backups with RCON consistency hooks.
| Field | Description | Default |
|---|---|---|
enabled | Enable backups | — |
schedule | Cron schedule for periodic backups | — |
beforeUpdate | Create backup before any server update | true |
volumeSnapshotClassName | VolumeSnapshotClass to use | cluster default |
retention.maxCount | Maximum snapshots to retain per server | 10 |
spec:
backup:
enabled: true
schedule: "0 */6 * * *" # Every 6 hours
beforeUpdate: true # Backup before updates
volumeSnapshotClassName: csi-hostpath-snapclass
retention:
maxCount: 10
When RCON is enabled, the operator uses RCON hooks (save-all, save-off, save-on) to ensure world data consistency before creating the VolumeSnapshot. Without RCON, snapshots are crash-consistent only — recent unsaved data may be lost.
Manual trigger:
kubectl annotate papermcserver my-server \
mc.k8s.lex.la/backup-now="$(date +%s)" \
--namespace minecraft
pluginConfigs¶
Optional — Overrides plugin config files for this specific server. When a file path matches a plugin's default config, the server version wins (whole-file replacement).
| Field | Description |
|---|---|
pluginName | Name of the Plugin CRD (same namespace) |
configs[].configMapRef.name | ConfigMap name |
configs[].configMapRef.key | Key within the ConfigMap |
configs[].path | Target path relative to plugin directory |
configs[].overwrite | always (default) or ifNotExists |
spec:
pluginConfigs:
- pluginName: bluemap
configs:
- configMapRef:
name: prod-bluemap
key: core.conf
path: core.conf
overwrite: always
Merge Priority
For a given plugin file path, the resolution order is:
- Server override (
pluginConfigs[pluginName].configs[path]) — if present, used - Plugin default (
Plugin.spec.configs[path]) — fallback - No config — file not managed by the operator
There is no deep merge. The entire file is replaced.
serverConfigs¶
Optional — Server-level config files placed relative to the workdir (/data). Use this for server.properties, paper-global.yml, bukkit.yml, and similar files.
| Field | Description |
|---|---|
configMapRef.name | ConfigMap name (same namespace) |
configMapRef.key | Key within the ConfigMap |
path | Target path relative to /data |
overwrite | always (default) or ifNotExists |
spec:
serverConfigs:
- configMapRef:
name: mc-server-config
key: server.properties
path: server.properties
overwrite: always
- configMapRef:
name: mc-server-config
key: paper-global.yml
path: config/paper-global.yml
overwrite: ifNotExists
Implementation Detail
Config files are injected via a config-injector init container that runs before the PaperMC container. The init container uses busybox:1.37 and executes a generated shell script that copies files from ConfigMap volumes to the data PVC.
The script ConfigMap ({server-name}-config-script) is managed by the operator and automatically updated when configs change.
podTemplate¶
Required — Template for the StatefulSet pod.
This is a standard Kubernetes PodTemplateSpec. Key fields:
| Field | Description |
|---|---|
spec.containers[0].resources | CPU/memory requests and limits |
spec.containers[0].env | Environment variables |
spec.volumes | Additional volumes |
spec:
podTemplate:
spec:
containers:
- name: minecraft
resources:
requests:
memory: "2Gi"
cpu: "500m"
limits:
memory: "4Gi"
env:
- name: JAVA_OPTS
value: "-Xmx3G -Xms1G"
volumes:
- name: config
configMap:
name: server-config
Status Fields¶
The operator updates the status to reflect the current state.
currentVersion / currentBuild¶
Currently running Paper version and build.
desiredVersion / desiredBuild¶
Target version the operator wants to run (resolved from updateStrategy).
plugins¶
List of matched Plugin resources and their versions.
status:
plugins:
- pluginRef:
name: essentialsx
namespace: minecraft
resolvedVersion: "2.21.0"
currentVersion: "2.20.1"
desiredVersion: "2.21.0"
compatible: true
source: hangar
availableUpdate¶
Next available update if any.
status:
availableUpdate:
version: "1.21.4"
build: 130
releasedAt: "2024-01-15T10:00:00Z"
foundAt: "2024-01-16T03:00:00Z"
plugins:
- pluginRef:
name: essentialsx
namespace: minecraft
version: "2.21.0"
lastUpdate¶
Record of the most recent update attempt.
updateBlocked¶
Indicates if updates are blocked due to compatibility issues.
status:
updateBlocked:
blocked: true
reason: "Plugin incompatible with target version"
blockedBy:
plugin: "old-plugin"
version: "1.0.0"
supportedVersions:
- "1.20.4"
- "1.20.6"
backup (status)¶
Observed backup state for the server.
status:
backup:
backupCount: 5
lastBackup:
snapshotName: "survival-backup-1708742400"
startedAt: "2026-02-24T00:00:00Z"
completedAt: "2026-02-24T00:00:05Z"
successful: true
trigger: "scheduled"
| Field | Description |
|---|---|
backupCount | Current number of retained VolumeSnapshots |
lastBackup.snapshotName | Name of the VolumeSnapshot resource (empty when backup failed before snapshot creation) |
lastBackup.startedAt | When the backup process started |
lastBackup.completedAt | When the backup completed |
lastBackup.successful | Whether the backup succeeded |
lastBackup.trigger | What triggered the backup (scheduled, before-update, manual) |
conditions¶
Standard Kubernetes conditions.
| Type | Description |
|---|---|
Ready | Server reconciled successfully |
StatefulSetReady | StatefulSet has ready replicas |
UpdateAvailable | New Paper version/build available |
UpdateBlocked | Update blocked by plugin incompatibility |
Updating | Update currently in progress |
SolverRunning | Constraint solver is executing |
CronScheduleValid | Maintenance window cron is valid |
BackupCronValid | Backup cron schedule is valid |
BackupReady | VolumeSnapshot API is available for backups |
ConfigInjectionReady | All referenced ConfigMaps for config injection are available |
Complete Example¶
apiVersion: mc.k8s.lex.la/v1beta1
kind: PaperMCServer
metadata:
name: survival
namespace: minecraft
labels:
environment: production
server-type: survival
spec:
updateStrategy: "auto"
updateDelay: "168h" # Wait 7 days
updateSchedule:
checkCron: "0 3 * * *"
maintenanceWindow:
enabled: true
cron: "0 4 * * 0"
gracefulShutdown:
timeout: 300s
backup:
enabled: true
schedule: "0 */6 * * *"
beforeUpdate: true
volumeSnapshotClassName: "csi-hostpath-snapclass"
retention:
maxCount: 10
rcon:
enabled: true
passwordSecret:
name: survival-rcon
key: password
network:
networkPolicy:
enabled: true
restrictEgress: true
gateway:
enabled: true
parentRefs:
- name: minecraft-gateway
namespace: gateway-system
tcpRoute:
enabled: true
service:
type: LoadBalancer
annotations:
external-dns.alpha.kubernetes.io/hostname: survival.minecraft.example.com
podTemplate:
spec:
containers:
- name: minecraft
resources:
requests:
memory: "4Gi"
cpu: "1"
limits:
memory: "8Gi"
env:
- name: JAVA_OPTS
value: "-Xmx6G -Xms2G -XX:+UseG1GC"
nodeSelector:
minecraft: "true"
tolerations:
- key: "minecraft"
operator: "Exists"
effect: "NoSchedule"
See Also¶
- Plugin — Plugin CRD reference
- Update Strategies — Detailed version management guide
- Troubleshooting — Common issues