Skip to main content
Every grounds push --target=staging creates a preview environment: a fresh namespace, a fresh deployment, and a public URL. They’re built for sharing — review apps, demos, end-to-end test runs against a real cluster. For the step-by-step staging workflow, see Share staging previews.

Anatomy of a preview env

The TTL clock starts at preview creation, not at push completion. A pinned preview has no TTL.

Listing your previews

Add --include-deleted to see envs that were swept by the janitor.

Pinning

Pinning skips the TTL janitor — the env stays alive until you unpin or delete it explicitly. Useful for long-running demos or a staging environment a non-developer is using.
In the portal, the Previews page has a per-row dropdown with Pin (skip auto-cleanup) / Unpin (re-enable cleanup).
Pinning indefinitely costs cluster resources. Pin sparingly — for demos that have an end date — and unpin when done. The platform team will follow up on long-pinned envs that look forgotten.

Lifecycle internals

When you push to staging, forge:
  1. Builds your image (same as dev).
  2. Creates a PreviewEnv row with a generated short ID.
  3. Provisions a preview-<id> namespace with quota + default-deny NetworkPolicy.
  4. Deploys the workload into that namespace.
  5. Configures an ingress at <name>-pr<id>.mc.grnds.io (Minecraft workloads) or …dev.grnds.io (service workloads).
The PreviewJanitor runs every 30 minutes:
  • Lists all PreviewEnv rows where expiresAt < now() AND deletedAt IS NULL AND pinned = false.
  • Deletes the namespace (which cascades the deployment, ingress, etc.).
  • Sets deletedAt on the row so it stops being considered.
Deleted preview envs remain in the database for audit; the portal hides them by default but grounds preview list --include-deleted will show them.

When not to use staging

  • Inner-loop iteration on your own machine — use groundsTestLocal instead. It’s offline, free, and 5 seconds per cycle. See Local development.
  • Long-lived dev environments for one person — that’s what --target=dev is for. It doesn’t burn a fresh namespace per push.