> ## Documentation Index
> Fetch the complete documentation index at: https://grounds-docs-platform-architecture.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Manifest reference

> Every field in grounds.yaml, what it does, and what's optional.

`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

```yaml grounds.yaml theme={null}
name: my-plugin
type: plugin-paper
baseImage: paper
```

## Full example

```yaml grounds.yaml theme={null}
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`](#plugins-optional) 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:

| 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.                                                                                                     |

Forge resolves the source key to the current stable version in the [base image catalog](/build/concepts/base-images) when it builds your push. Do not put an OCI tag or digest in `grounds.yaml`; promote a new catalog version instead.

<Note>
  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.
</Note>

### `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](/build/multi-plugin-stacks).

A working end-to-end example lives at [groundsgg/multi-plugin-sample](https://github.com/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`:

```yaml theme={null}
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](/build/cli/workspace) when you pass `--local` or
`--with-local`.

Each `source` is one of three source kinds, distinguished by prefix:

```yaml theme={null}
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
```

| 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/`    |

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).

<Tip>
  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.
</Tip>

#### 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.

```yaml theme={null}
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.

```yaml theme={null}
agones:
  replicas: 3
```

| 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. |

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:

| 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 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.
