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
Full example
grounds.yaml
plugins below.
Fields
name (required)
The logical app name. Used as:
- The Kubernetes
Deploymentname (so it has to match[a-z0-9-]{1,40}). - The hostname prefix for staging previews (
<name>-pr<id>.mc.grnds.iofor Minecraft workloads,…dev.grnds.ioforservice). - The display name in the portal.
#grounds-platform if you need to migrate state.
type (required)
What kind of workload this is. One of:
| Type | Workload | Base image source keys |
|---|---|---|
plugin-paper | Bukkit/Paper plugin JAR | paper |
plugin-velocity | Velocity plugin JAR | velocity |
gamemode | Bukkit/Paper plugin JAR running as an Agones-managed gameserver | paper-gamemode |
service | Plain 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:
baseImage | Catalog source | What it gives you |
|---|---|---|
paper | ghcr.io/groundsgg/paper | A Paper server with sane defaults, your JAR auto-loaded into plugins/. Use with type: plugin-paper. |
paper-gamemode | ghcr.io/groundsgg/paper-gamemode | Same 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. |
velocity | ghcr.io/groundsgg/velocity | A Velocity proxy with your JAR in plugins/. |
service | ghcr.io/groundsgg/jvm-service | Generic JVM runner — your JAR’s main is the entrypoint, no Minecraft runtime. |
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:
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:
| Kind | Form | Resolved by |
|---|---|---|
| Local | a path | groundsPush reads from disk |
| Gradle project | :project (or :nested:project) | groundsPush looks up the project’s shadowJar / jar task and depends on it automatically |
| GitHub release | github:<owner>/<repo>@<tag>[:<asset>] | groundsPush downloads from the GitHub Releases API into ~/.gradle/caches/grounds-push/ |
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).
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.jaron the release is used. - SHA256 verification — when a release ships a
<asset>.sha256sidecar,groundsPushfetches it and verifies before caching. Releases without one are accepted unchanged. - Auth — public repos work without auth; private repos read
GITHUB_TOKENfrom the environment. A 404 with no token in scope surfaces a hint about it.
<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-jarENTRYPOINT; 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.).groundsPushdiscoversshadowJarfirst, then falls back tojar. - 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:
- CLI flag:
grounds push --target=staging - Gradle extension:
groundsPush { target.set("staging") } grounds.yaml: target: ...- Fallback:
dev
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.
- 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.
| Field | Default | Range | What it does |
|---|---|---|---|
replicas | 1 | 1–20 | Number 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. |
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 parsesgrounds.yaml before upload. Common errors:
| Error | Fix |
|---|---|
missing required field 'name' | Add name. |
field 'name' must be a string | Quote numeric-looking names. |
'resources' must be a mapping | Make 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 forgrounds.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.