Skip to content

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)
spec:
  source:
    type: hangar
    project: "EssentialsX/Essentials"

Supported Sources

Currently implemented:

  • hangar — PaperMC Hangar (hangar.papermc.io)
  • url — Direct URL download (GitHub releases, private repos, etc.)

Planned (not yet implemented):

  • modrinth#2
  • spigot#3

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
spec:
  updateStrategy: "latest"

version

Optional — Target plugin version. Required for pin and build-pin strategies.

spec:
  updateStrategy: "pin"
  version: "2.20.1"

build

Optional — Target build number. Only used with build-pin strategy.

spec:
  updateStrategy: "build-pin"
  version: "2.20.1"
  build: 456

updateDelay

Optional — Grace period before applying new plugin versions.

spec:
  updateDelay: "72h"  # Wait 3 days before using new releases

instanceSelector

Required — Label selector to match PaperMCServer instances.

spec:
  instanceSelector:
    matchLabels:
      environment: production
      server-type: survival

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)
spec:
  endpoints:
    - name: web-ui
      port: 8123
      protocol: HTTP
    - name: metrics
      port: 9100
      protocol: 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.httpRoutes configured
  • 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.

spec:
  pluginDirName: BlueMap

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
spec:
  compatibilityOverride:
    enabled: true
    minecraftVersions:
      - "1.20.4"
      - "1.20.6"
      - "1.21.1"

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.

status:
  lastFetched: "2024-01-15T10:00:00Z"

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:

  1. The operator marks the plugin for deletion on matched servers
  2. During next server restart, the JAR file is removed from /data/plugins/
  3. Once all JARs are cleaned up, the Plugin resource is fully deleted

Check deletion progress:

kubectl get plugin essentialsx -o yaml
status:
  deletionProgress:
    - serverName: survival
      namespace: minecraft
      jarDeleted: true
      deletedAt: "2024-01-15T04:00:00Z"
    - serverName: creative
      namespace: minecraft
      jarDeleted: false

See Also