guides

Pause, suspend, and restore (lifecycle)

Free guest CPUs or host RAM without losing a persistent lab.

Pause vs suspend

CommandProcessHost RAMStatusResume with
grain pauseQEMU stays upStill heldpausedgrain resume
grain suspendQEMU stoppedFreedsuspendedgrain restore
grain stopStoppedFreedephemeral deleted / persistent stoppedgrain start

Pause freezes vCPUs via QMP (stop / cont). Good for a short break without tearing down networking.

Suspend applies to persistent VMs only. grain tries a qcow2 savevm snapshot when it can, then stops the process. Restore loads that snapshot if present; otherwise it cold-boots the disk.

bash
grain new -p -n lab
# ... work ...
grain suspend lab
# hours later
grain restore lab

Rules of thumb

  • Ephemeral VMs: prefer rm or stop (they are disposable)
  • Persistent labs: suspend / restore or stop / start
  • start rejects a suspended VM — use restore
  • Suspended VMs do not count toward running resource caps

Create path and latency

PathWhat happensTypical ready time
Cold grain newNew disk/seed + QEMU + guest boot + agent~seconds on grain-ubuntu (host work ~200 ms)
Spawn grain new --from TEMPLATEClone disk + start (-loadvm if snapshotted)Sub-second–few seconds when suspended
Pool claim grain new --from-poolRename ready member + start (or rename-only if running)Fastest assign path

Daemon INFO logs help measure:

  • create timing — image / disk / seed / start / wait ms
  • spawn timing — from-template path
  • pool claim timing — claim + start (or running_mode=true)

Lean cold path (already default for goldens): agent-ready images use minimal cloud-init; growpart/resizefs is skipped unless the clone disk is larger than the base (grow deferred after agent). Agent wait polls at 50 ms.

The product <100 ms ready promise means assign a ready sandbox (pool/snapshot), not “full Ubuntu cold boot in 100 ms.”

Fast create: spawn and warm pool

bash
# Template once
grain new -i grain-ubuntu -n golden -p --wait agent
grain suspend golden

# Spawn = clone + start (clone cost every time)
grain new --from golden -n w1

# Warm pool = pre-cloned members; claim renames + starts (or rename-only if running)
# config.yaml:
#   warm_pool:
#     template: golden
#     size: 2
#     running: false   # true = keep members agent-ready (uses host RAM)
grain pool fill
grain new --from-pool -n w2
grain pool status
grain pool drain   # delete ready members
ModeMembersClaim
Default (running: false)Stopped/suspended disks (no host RAM)Start with -loadvm when snapshotted
running: trueAgent-ready, uses host RAMRename/untag only

The daemon refills toward warm_pool.size after each claim; on grain up it also fills in the background when configured.

Desktop warm-path loop

  1. Boot and prepare a golden: create persistent sandbox, wait for agent, then More → Promote to golden + fill pool (suspends if running, sets warm_pool.template / size, restarts local daemon, fills).
  2. Or set Settings → Warm pool (template, size, optional running mode) → Apply warm poolFill pool.
  3. New sandbox prefers From warm pool when ready > 0; empty/unconfigured states stay honest (cold boot message, no silent slow path).
  4. Multi-select Start runs a capacity preflight against host caps (max_vms / CPU / memory from the active daemon’s GET /info).
  5. Activity drawer can filter by source (desktop / cli / mcp / api).

Full Desktop surface: Grain Desktop.

API

  • POST /vms/{name}/pause · resume · suspend · restore · clone
  • POST /vms body from — spawn from template
  • POST /vms body from_pool: true — claim from warm pool
  • GET /pool · POST /pool/fill · POST /pool/claim · POST /pool/drain
  • GET /activity — control-plane activity ring
  • GET /info — version + resource caps (strings)