Skip to content

GCP (nested virt)

GCP is the default ephemeral provider. N2 / C3 / M-series machines support nested virt; we enable it via --enable-nested-virtualization and Firecracker runs in the guest.

Verified end-to-end 2026-05-06 on n2-standard-4 in us-central1-a. Cycle ≈ 25 min wall, ≈ $0.10 in GCP charges.

  • INSTANCE_TYPE=n2-standard-4 (~$0.19/hr in us-central1)
  • REGION=us-central1-a
  • BOOT_DISK_SIZE=50GB (GCE default 10GB is too small)
  • Boot disk type pd-balanced
  • Image ubuntu-2404-lts-amd64 from ubuntu-os-cloud
  • gcloud CLI authenticated: gcloud auth login
  • A project selected with billing enabled and the Compute API on:
Terminal window
gcloud config set project <id>
gcloud services enable compute.googleapis.com
  • An SSH pubkey at ~/.ssh/id_ed25519.pub (or set SSH_PUBKEY=<path>).
Terminal window
PROVIDER=gcp bash ops/ephemeral/up.sh
# ... 5–15 min ...
ssh mvm@<ip> 'bash ~/smoke-mvmd.sh'
ssh mvm@<ip> 'bash ~/run-tests.sh'
PROVIDER=gcp LABEL=<label> bash ops/ephemeral/down.sh
  1. GCE doesn’t auto-inject SSH keys to root the way Hetzner does. providers/gcp.sh injects via --metadata=ssh-keys=root:<pubkey> from ${SSH_PUBKEY:-~/.ssh/id_ed25519.pub}.
  2. GCE default boot disk is 10GB, far too small for cloud-init’s apt + rustup + Nix + cargo-tools + mvmd build. Bumped to 50GB.
  3. Provider-IP reuse leaves stale ~/.ssh/known_hosts entries that accept-new won’t override. up.sh uses StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null.
  4. NAT idle timeouts reap the SSH session during long cloud-init status --wait. up.sh uses ServerAliveInterval=30 ServerAliveCountMax=240.
  5. mvm-hostd lives in mvmd-runtime, not the root crate. cargo build --bin mvm-hostd from the workspace root fails. post-deploy.sh uses cargo build --release --package mvmd-runtime --bin mvm-hostd.
  6. mvmd-coordinator validator rejects [autoscale] enabled = false. Template ships enabled = true with min=max=1 nodes (functional no-op for a single-node ephemeral host).