Plugin¶
The Plugin CRD defines a Minecraft plugin to be installed on matched servers.
Overview¶
apiVersion: mc.k8s.lex.la/v1beta1
kind: Plugin
metadata:
name: essentialsx
namespace: minecraft
spec:
source:
type: hangar
project: "EssentialsX/Essentials"
updateStrategy: "latest"
instanceSelector:
matchLabels:
environment: production
Spec Fields¶
source¶
Required — Defines where to fetch the plugin.
| Field | Description |
|---|---|
type | Repository type (see supported sources below) |
project | Plugin identifier (for hangar) |
url | Direct download URL (for type: url) |
checksum | Optional SHA256 hash for integrity verification (for type: url) |
Supported Sources
Currently implemented:
hangar— PaperMC Hangar (hangar.papermc.io)url— Direct URL download (GitHub releases, private repos, etc.)
Planned (not yet implemented):
URL Source¶
For plugins not published on marketplaces, use type: url with a direct HTTPS download link.
The operator downloads the JAR and extracts metadata (name, version, API version) from plugin.yml or paper-plugin.yml inside the archive. If extraction fails, spec.version is used as fallback.
spec:
source:
type: url
url: "https://github.com/example/plugin/releases/download/v1.0.0/plugin-1.0.0.jar"
checksum: "aabbccdd00112233aabbccdd00112233aabbccdd00112233aabbccdd00112233"
Checksum Verification
The checksum field accepts a SHA256 hex string (64 characters). If provided, the operator verifies the downloaded JAR against this hash. If omitted, the operator logs a warning but proceeds with the unverified download.
Update Strategy for URL Sources
For URL sources, there is only one version (the JAR at the URL), so strategy differences only affect how the solver processes compatibility. Use latest for simplicity. Note that updateDelay is measured from the time the operator first downloads the JAR, not from the plugin's actual release date. Validation happens at reconciliation time, not at CR creation time (webhook validation is planned).
URL Metadata Caching
The operator caches URL plugin metadata for 1 hour to avoid re-downloading the JAR on every reconciliation cycle. The cache is invalidated when spec.source.url or spec.source.checksum changes. Changes to spec.version (the fallback version) take effect when the cache expires.
updateStrategy¶
Optional — Defines how plugin versions are managed. Default: latest.
| Value | Description |
|---|---|
latest | Always use newest version from repository |
auto | Constraint solver picks best version compatible with servers |
pin | Pin to specific version (requires version field) |
build-pin | Pin to specific version and build |
version¶
Optional — Target plugin version. Required for pin and build-pin strategies.
build¶
Optional — Target build number. Only used with build-pin strategy.
updateDelay¶
Optional — Grace period before applying new plugin versions.
instanceSelector¶
Required — Label selector to match PaperMCServer instances.
Or with expressions:
spec:
instanceSelector:
matchExpressions:
- key: environment
operator: In
values:
- production
- staging
endpoints¶
Optional — Network endpoints exposed by the plugin. Each endpoint creates a port on matched servers' Services.
Useful for plugins with web interfaces (Dynmap, BlueMap, Plan) or custom protocols.
| Field | Description |
|---|---|
name | Unique endpoint name within the plugin (DNS label format) |
port | Port number (1-65535) |
protocol | TCP, UDP, or HTTP (default: TCP) |
Port Handling
- TCP/HTTP endpoints create a TCP Service port
- UDP endpoints create a UDP Service port
- HTTP endpoints additionally enable HTTPRoute creation on servers with
gateway.httpRoutesconfigured - Port name format:
ep-{port}-{proto}(e.g.,ep-8123-tcp)
pluginDirName¶
Optional — The plugin's directory name under plugins/. Must match the name field in the plugin's plugin.yml. Defaults to source.project if not set. Required when configs are specified.
Directory Name Matching
The pluginDirName must exactly match the directory that the plugin creates under plugins/. Check the plugin's plugin.yml for the correct name field. An incorrect value will cause config files to be placed in the wrong directory.
configs¶
Optional — Default config files for this plugin. Files are copied to plugins/{pluginDirName}/ on matched servers via an init container.
Servers can override specific files via spec.pluginConfigs.
| Field | Description |
|---|---|
configMapRef.name | ConfigMap name (same namespace) |
configMapRef.key | Key within the ConfigMap |
path | Target path relative to plugin directory |
overwrite | always (default) or ifNotExists |
spec:
pluginDirName: BlueMap
configs:
- configMapRef:
name: bluemap-defaults
key: core.conf
path: core.conf
overwrite: always
- configMapRef:
name: bluemap-defaults
key: overworld.conf
path: maps/overworld.conf
overwrite: ifNotExists
Overwrite Policy
always: The file is replaced on every pod restart. The ConfigMap is the source of truth. Manual edits on the PVC are lost.ifNotExists: The file is only written if it doesn't already exist on the PVC. This preserves manual edits made inside the running container.
Path Validation
- Paths must be relative (no leading
/) - Paths must not contain
..(no path traversal) - Subdirectories are created automatically (e.g.,
maps/overworld.conf) - Duplicate paths within the same plugin are rejected by the webhook
compatibilityOverride¶
Optional — Manual compatibility specification for plugins without proper metadata.
| Field | Description |
|---|---|
enabled | Enable the override |
minecraftVersions | List of compatible Minecraft versions |
Use Sparingly
Only use compatibility overrides when the plugin repository lacks version metadata. Incorrect overrides may cause plugin compatibility issues.
Status Fields¶
availableVersions¶
Cached metadata from the plugin repository.
status:
availableVersions:
- version: "2.21.0"
minecraftVersions:
- "1.20.4"
- "1.21.1"
downloadURL: "https://..."
hash: "aabbccdd00112233aabbccdd00112233aabbccdd00112233aabbccdd00112233"
cachedAt: "2024-01-15T10:00:00Z"
releasedAt: "2024-01-10T12:00:00Z"
matchedInstances¶
Servers matched by the instanceSelector.
status:
matchedInstances:
- name: survival
namespace: minecraft
version: "1.21.4"
compatible: true
- name: creative
namespace: minecraft
version: "1.20.4"
compatible: true
repositoryStatus¶
Plugin repository availability.
| Value | Description |
|---|---|
available | Repository accessible, metadata current |
unavailable | Repository temporarily unreachable |
orphaned | Repository removed, using cached data |
lastFetched¶
Timestamp of the last successful API fetch.
conditions¶
Standard Kubernetes conditions.
| Type | Description |
|---|---|
Ready | Plugin reconciled successfully |
RepositoryAvailable | Plugin repository is accessible |
VersionResolved | Metadata fetched and servers matched |
Complete Example¶
apiVersion: mc.k8s.lex.la/v1beta1
kind: Plugin
metadata:
name: bluemap
namespace: minecraft
spec:
source:
type: hangar
project: "BlueMap/BlueMap"
updateStrategy: "latest"
updateDelay: "168h" # Wait 7 days
# Match all production servers
instanceSelector:
matchLabels:
environment: production
# Expose BlueMap web interface
endpoints:
- name: web-ui
port: 8100
protocol: HTTP
---
apiVersion: mc.k8s.lex.la/v1beta1
kind: Plugin
metadata:
name: essentialsx
namespace: minecraft
spec:
source:
type: hangar
project: "EssentialsX/Essentials"
updateStrategy: "pin"
version: "2.20.1"
instanceSelector:
matchLabels:
environment: production
---
apiVersion: mc.k8s.lex.la/v1beta1
kind: Plugin
metadata:
name: worldedit
namespace: minecraft
spec:
source:
type: hangar
project: "EngineHub/WorldEdit"
updateStrategy: "auto"
instanceSelector:
matchExpressions:
- key: server-type
operator: In
values:
- creative
- build
---
apiVersion: mc.k8s.lex.la/v1beta1
kind: Plugin
metadata:
name: custom-plugin
namespace: minecraft
spec:
source:
type: url
url: "https://github.com/example/plugin/releases/download/v1.2.0/plugin-1.2.0.jar"
checksum: "aabbccdd00112233aabbccdd00112233aabbccdd00112233aabbccdd00112233"
version: "1.2.0" # Fallback if plugin.yml extraction fails
updateStrategy: "latest"
instanceSelector:
matchLabels:
environment: production
Plugin Deletion¶
When a Plugin is deleted:
- The operator marks the plugin for deletion on matched servers
- During next server restart, the JAR file is removed from
/data/plugins/ - Once all JARs are cleaned up, the Plugin resource is fully deleted
Check deletion progress:
status:
deletionProgress:
- serverName: survival
namespace: minecraft
jarDeleted: true
deletedAt: "2024-01-15T04:00:00Z"
- serverName: creative
namespace: minecraft
jarDeleted: false
See Also¶
- PaperMCServer — Server CRD reference
- Update Strategies — Version management guide
- Architecture — Constraint solver details