Skip to main content

Documentation Index

Fetch the complete documentation index at: https://grounds-feat-grounds-runtime-libraries.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

grounds.yaml lives at the root of your project and tells forge what you’re pushing. It’s read by both the Gradle plugin (to upload it) and forge (to build + deploy it).

Minimal example

grounds.yaml
name: my-plugin
type: plugin-paper
baseImage: paper

Full example

grounds.yaml
name: my-plugin
type: plugin-paper
baseImage: paper
jar: build/libs/my-plugin-*-all.jar
target: dev          # optional default; CLI/Gradle override wins
resources:
  cpu: 500m
  memory: 1Gi
Multi-plugin servers ship multiple JARs onto one Paper / Velocity / gamemode workload — see plugins below.

Fields

name (required)

The logical app name. Used as:
  • The Kubernetes Deployment name (so it has to match [a-z0-9-]{1,40}).
  • The hostname prefix for staging previews (<name>-pr<id>.mc.grnds.io for Minecraft workloads, …dev.grnds.io for service).
  • The display name in the portal.
Once you’ve pushed, don’t rename — that creates a new app from forge’s perspective and orphans the previous one. Rename via portal or contact #grounds-platform if you need to migrate state.

type (required)

What kind of workload this is. One of:
TypeWorkloadBase image source keys
plugin-paperBukkit/Paper plugin JARpaper
plugin-velocityVelocity plugin JARvelocity
gamemodeBukkit/Paper plugin JAR running as an Agones-managed gameserverpaper-gamemode
servicePlain JVM app (no Minecraft runtime)service
type drives quotas, resource defaults, port mappings, and which container our renderer wires up.

baseImage (required)

The catalog source key for the runtime image your JAR is layered onto. Must be one of the base image sources allowed for your type:
baseImageCatalog sourceWhat it gives you
paperghcr.io/groundsgg/paperA Paper server with sane defaults, your JAR auto-loaded into plugins/. Use with type: plugin-paper.
paper-gamemodeghcr.io/groundsgg/paper-gamemodeSame as paper, but pre-bundled with plugin-agones-paper so the SDK sidecar (injected on every Fleet pod) reports Allocated/Ready from player events. Use with type: gamemode.
velocityghcr.io/groundsgg/velocityA Velocity proxy with your JAR in plugins/.
serviceghcr.io/groundsgg/jvm-serviceGeneric JVM runner — your JAR’s main is the entrypoint, no Minecraft runtime.
Forge resolves the source key to the current stable version in the base image catalog when it builds your push. Do not put an OCI tag or digest in grounds.yaml; promote a new catalog version instead.
The valid source keys come from the platform catalog. The table above lists the standard sources, but self-hosted or internal environments may expose additional sources.

jar (optional, default build/libs/*.jar)

A glob pointing at the JAR to upload, relative to the project root. The Gradle plugin auto-detects shadowJar or jar and sets this for you, so you usually don’t need to write it. When to set it explicitly:
  • You produce multiple JARs and want to ensure the right one is picked.
  • You build with something other than shadowJar / jar.

plugins (optional)

A list of 2–10 plugin sources bundled into a single Paper / Velocity / gamemode server. Useful when you want to test plugin interaction (economy + chat + teams) on one running instance instead of pushing each plugin as its own server. For the internal workflow that uses these fields with local overrides, see Test multi-plugin stacks. A working end-to-end example lives at groundsgg/multi-plugin-sample — two trivial Paper plugins bundled via :gradle-project refs. Clone, run ./gradlew groundsPush, see both plugins boot on one server. For team workflows, prefer structured entries with id, optional variant, and source:
name: velocity-stack
type: plugin-velocity
baseImage: velocity
plugins:
  - id: plugin-agones
    variant: velocity
    source: github:groundsgg/plugin-agones@v0.5.0
  - id: plugin-player
    variant: velocity
    source: github:groundsgg/plugin-player@v0.1.0
source is the portable default used by plain grounds push. id and variant let the CLI match the entry to a local workspace override when you pass --local or --with-local. Each source is one of three source kinds, distinguished by prefix:
name: combo
type: plugin-paper
baseImage: paper
plugins:
  - id: economy
    source: modules/economy/build/libs/economy.jar           # local (relative)
  - id: legacy
    source: /opt/jars/legacy.jar                             # local (absolute)
  - id: chat
    source: :chat                                            # Gradle project
  - id: teams
    source: github:groundsgg/plugin-teams@v0.3.0             # GitHub release, first .jar asset
  - id: shop
    source: github:groundsgg/plugin-shop@v1.4.2:shop-all.jar # GitHub release, named asset
KindFormResolved by
Locala pathgroundsPush reads from disk
Gradle project:project (or :nested:project)groundsPush looks up the project’s shadowJar / jar task and depends on it automatically
GitHub releasegithub:<owner>/<repo>@<tag>[:<asset>]groundsPush downloads from the GitHub Releases API into ~/.gradle/caches/grounds-push/
The Gradle plugin packs all resolved JARs into a tar.gz and forge unpacks them into /app/plugins/ in manifest order (a numeric prefix preserves load order, since Paper’s pluginsFolder scan is filesystem-ordered).
Keep local filesystem paths out of shared manifests when multiple developers work in separate repositories. Put release sources in grounds.yaml and put machine-specific paths in workspace.yaml instead.

GitHub source rules

  • Owner allowlist — only groundsgg/* is accepted. Other owners are rejected at parse time on the client and re-validated server-side at push admission. Hard security boundary, not configurable.
  • Tag pinning — the tag must be a SemVer-shaped tag (v1.4.2, optional pre-release / build metadata) or a 40-char commit SHA. latest, branch names, and partial SHAs are intentionally rejected so bundles stay reproducible.
  • Asset filter — the asset must end in .jar. No source tarballs, no zips. If you don’t name an asset, the first .jar on the release is used.
  • SHA256 verification — when a release ships a <asset>.sha256 sidecar, groundsPush fetches it and verifies before caching. Releases without one are accepted unchanged.
  • Auth — public repos work without auth; private repos read GITHUB_TOKEN from the environment. A 404 with no token in scope surfaces a hint about it.
Cache key is <owner>-<repo>-<tag>-<asset>.jar. Because tags are immutable pins, a cache hit means the same bytes as before — no re-download per push.

Constraints

  • Mutually exclusive with jar: — pick one shape per manifest.
  • Forbidden for type: service (services have a single-jar ENTRYPOINT; multi-plugin has no meaningful semantics there).
  • 2 minimum (single-plugin uses jar:), 10 maximum, 100 MB total upload after resolution.
  • For Gradle project refs the subproject must apply a Jar-producing plugin (java, com.gradleup.shadow, etc.). groundsPush discovers shadowJar first, then falls back to jar.
  • Absolute local paths still work but log a “non-portable manifest” warning, because CI won’t see your machine’s filesystem.

target (optional)

A default target if neither the CLI flag nor the Gradle extension overrides it. Order of precedence:
  1. CLI flag: grounds push --target=staging
  2. Gradle extension: groundsPush { target.set("staging") }
  3. grounds.yaml: target: ...
  4. Fallback: dev
You almost never want this in version control — it makes “what does pushing actually do?” non-obvious for teammates. Prefer the CLI flag.

resources (optional)

CPU/memory request hints. They’re suggestions — forge clamps them to the per-type minimums (e.g., plugin-paper is at least 250m CPU and 1Gi memory) and to per-project quota maxima. If your value falls inside the legal range, it’s used as-is.
resources:
  cpu: 500m       # Kubernetes resource units: 500m = 0.5 vCPU
  memory: 1Gi
Quirks worth knowing:
  • We always set requests == limits. Pods are deterministic and don’t burst.
  • Below the minimum: silently raised to the minimum.
  • Above the project quota: push fails at admission with a quota error.

agones (optional, gamemode only)

Agones-specific knobs. Only meaningful for type: gamemode, where the renderer wraps your workload in an Agones Fleet. Other types ignore the block.
agones:
  replicas: 3
FieldDefaultRangeWhat it does
replicas1120Number of GameServer replicas the Fleet keeps Ready. mc-router load-balances Minecraft traffic across them via the workload’s Service. Raise this when one game server can’t hold the player count you need; values above 20 require a platform-side quota bump.
The Agones SDK sidecar is injected by the cluster’s mutating webhook on every Fleet-managed pod, so plugin-agones-paper (and the Minestom/Velocity equivalents) can talk to localhost:9358 and report Allocated/Ready state from player events.

Other fields

grounds.yaml is strict. Unknown top-level fields are rejected at parse time so typos don’t silently get ignored. If you need something not covered here, open a #grounds-platform ticket — the parser only knows what’s listed above.

Validation

The Gradle plugin parses grounds.yaml before upload. Common errors:
ErrorFix
missing required field 'name'Add name.
field 'name' must be a stringQuote numeric-looking names.
'resources' must be a mappingMake sure it’s resources:\n cpu: … not resources: 500m.
unknown baseImage 'forge'Use one of the supported base images above.

Editor support

There’s no JSON Schema for grounds.yaml yet. On the roadmap; for now use this page as the reference. The Gradle plugin’s parse error messages name the offending field and line number.