A command-line tool that relentlessly tries to create a Google Compute Engine VM — typically with a GPU attached — until capacity is found.
On-demand GPUs are frequently out of stock. Creating a VM fails with a capacity error, and a stopped GPU VM often cannot be restarted because its zone ran dry in the meantime. gce-hunter automates both problems:
createhunts across every candidate zone, over and over, until one of them has capacity for your VM.startdetects the "stopped VM whose zone ran dry" situation and offers to hunt a replacement VM that boots from a snapshot of your current disk — in any zone — so none of your environment setup is lost.
Every cloud action shells out to the gcloud CLI, and every command line is
echoed to your terminal prefixed with $ before it runs, so you always see
exactly what is being done on your behalf.
gce-hunter drives the official Google Cloud CLI and never manages credentials itself. Before the first run:
- Install the Google Cloud SDK
gcloud auth login- Make sure the Compute Engine API is enabled on your project (the first
gcloud compute …call will print an activation link if it is not).
Every gce-hunter command starts with a preflight: it locates gcloud (on your
PATH, or the executable named in GCE_HUNTER_GCLOUD; on Windows the SDK's
gcloud.cmd shim is found too) and checks that an account is active
(gcloud config get-value account). If either check fails, the command stops
and tells you the fix. Credentials that expire in the middle of a hunt abort
it with a gcloud auth login hint.
curl -fsSL https://raw.githubusercontent.com/j-about/gce-hunter/master/install.sh | shinstall.sh detects your OS and CPU, downloads the newest
vX.Y.Z release, verifies its SHA-256 against the release's SHA256SUMS
file, and installs gce-hunter into /usr/local/bin — asking for your
password only if that directory is not writable. It ends by running
gce-hunter --version and warns you if /usr/local/bin is not on your PATH
or if gcloud cannot be found.
- Windows: run the line inside your Ubuntu (WSL) terminal — gce-hunter
must live where your
gcloudlives. For a gcloud installed on Windows itself, see the PowerShell steps below. - Chromebook (Linux/Crostini terminal): run
sudo apt-get install -y curlfirst ifcurlis missing. - Linux gets the fully static
x86_64-unknown-linux-muslbuild (works on any distro) oraarch64-unknown-linux-gnuon ARM machines.
| Variable | Effect |
|---|---|
GCE_HUNTER_VERSION=v0.1.0 |
install this exact release instead of the newest |
GCE_HUNTER_INSTALL_DIR=$HOME/.local/bin |
install somewhere else (no password needed) |
GCE_HUNTER_BASE_URL=https://… |
download the archives from a mirror instead of GitHub |
Uninstall with sudo rm /usr/local/bin/gce-hunter; the state file
can be deleted too.
Paste into PowerShell:
$tag = (Invoke-RestMethod "https://api.github.com/repos/j-about/gce-hunter/releases?per_page=100" |
Where-Object { $_.tag_name -match '^v\d+\.\d+\.\d+$' } | Select-Object -First 1).tag_name
$name = "gce-hunter-$tag-x86_64-pc-windows-msvc"
Invoke-WebRequest "https://github.com/j-about/gce-hunter/releases/download/$tag/$name.zip" -OutFile "$env:TEMP\$name.zip"
Expand-Archive "$env:TEMP\$name.zip" -DestinationPath $env:TEMP -Force
$dir = "$env:LOCALAPPDATA\Programs\gce-hunter"
New-Item -ItemType Directory -Force $dir | Out-Null
Copy-Item "$env:TEMP\$name\gce-hunter.exe" $dir -Force
$path = [Environment]::GetEnvironmentVariable("Path", "User")
if ($path -notlike "*$dir*") { [Environment]::SetEnvironmentVariable("Path", "$path;$dir", "User") }Then open a new terminal and run gce-hunter --version.
Each release on the releases page
ships one archive per target, named gce-hunter-<tag>-<target>.tar.gz
(.zip on Windows). It unpacks to a folder of the same name holding the
gce-hunter binary, this README and the LICENSE; put the binary somewhere on
your PATH. A SHA256SUMS file lists the hash of every archive: compare it
with sha256sum <archive> (Linux) or shasum -a 256 <archive> (macOS).
| Platform | Target |
|---|---|
| Linux x86_64, fully static — any distro, Chromebook (Crostini) | x86_64-unknown-linux-musl |
| Linux x86_64, glibc ≥ 2.35 | x86_64-unknown-linux-gnu |
| Linux ARM64, glibc ≥ 2.35 | aarch64-unknown-linux-gnu |
| macOS Intel | x86_64-apple-darwin |
| macOS Apple Silicon | aarch64-apple-darwin |
| Windows x86_64 | x86_64-pc-windows-msvc |
cargo install --path . --bin gce-hunter--bin is needed because the crate also builds mock-gcloud, the test
double used by the integration tests.
Every create parameter can be passed as a flag. When both stdin and stderr
are terminals, anything you omit is asked interactively. Otherwise — in
scripts, or with the output redirected — nothing ever prompts: --gpu-count,
--max-attempts and --delay fall back to their defaults, an absent
--region or --gpu-type means "all zones" / "no GPU", and every other
missing parameter is reported in one error listing all the missing flags.
Prompt suggestions never apply silently in scripts: they decide what the VM
costs.
The interactive prompts, in order:
- Google Cloud project — a Select over
gcloud projects list, starting on your active gcloud project (free-text entry if listing fails). - Region filter — no default; empty hunts worldwide.
- Attach a GPU? — defaults to Yes, then the GPU type (no default;
list them with
gcloud compute accelerator-types list) and count (1). - Machine type — a common default is prefilled. Each GPU type is only
available on certain machine families: see Google's
GPU machine types page, or
gcloud compute machine-types list. - Image family — no default — and image project (prefilled
according to the GPU choice). Browse the public images with
gcloud compute images list(--project <image-project> --no-standard-imagesfor a specific project); Google documents its public images and, for GPU-ready images, the Deep Learning VM images. - Boot disk size (suggested 100 GB, minimum 10) and type (
pd-balanced). - Instance name — suggested
<user>-vmfrom your gcloud account.
# Fully interactive:
gce-hunter create
# Fully scripted — a GPU VM hunted across every European zone that offers
# the GPU (fill in the values from Google's lists, see above):
gce-hunter create \
--project my-project \
--region europe \
--gpu-type <gpu-type> \
--machine-type <machine-type> \
--image-family <image-family> \
--image-project <image-project> \
--boot-disk-size 100 \
--boot-disk-type pd-balanced \
--name my-vm| Flag | Interactive | In scripts |
|---|---|---|
-p, --project |
Select over your projects | required |
-r, --region |
asked, no default | optional; --region "" also means no filter |
-g, --gpu-type |
"Attach a GPU?" then type | optional; absent = CPU-only VM |
--gpu-count |
1 |
1; needs --gpu-type; at least 1 |
-m, --machine-type |
asked, a default is prefilled | required |
--image-family |
asked, no default | required |
--image-project |
asked, prefilled according to the GPU choice | required |
--boot-disk-size |
suggested 100 (GB) |
required; at least 10 |
--boot-disk-type |
suggested pd-balanced |
required |
--name |
suggested <user>-vm |
required; lowercase letter first, then letters, digits or -, max 63 |
-n, --max-attempts |
0 = keep hunting forever |
same |
-d, --delay |
120 seconds between passes |
same; at least 1 |
--region is a zone-name prefix at any granularity — europe, europe-west,
europe-west1, europe-west1-b — with a numeric boundary: europe-west1
does not match europe-west10-a.
gce-hunter manages one VM at a time: create refuses to run while a VM
is recorded in the state file (delete clears it). GPU
VMs are always created with --maintenance-policy=TERMINATE (mandatory for
GPU instances) and --metadata=install-nvidia-driver=True (Deep Learning VM
images install the NVIDIA driver on first boot when it is not preinstalled).
How the hunt works. gce-hunter first asks gcloud which zones can host your
request (zones offering the GPU type, or all UP zones for CPU-only VMs) and
applies the --region prefix filter. Then it sweeps: one pass tries every
candidate zone once, in random order, with a 2-second courtesy delay between
tries. Each failed create is classified from gcloud's error text:
- Retried next pass — out of stock, rate-limited (HTTP 429), transient backend errors (5xx).
- Zone skipped for the rest of the hunt — the machine type or accelerator
is not offered there; the location is not accessible to your project
(allowlist-only regions such as
me-central2, or an organization policy). - Fatal, hunt aborted with the verbatim gcloud error plus a targeted
hint — quota exceeded (
GPUS_ALL_REGIONSdefaults to 0 on new projects: request an increase in the Google Cloud console, IAM & Admin → Quotas), permission or billing problems, expired credentials, image family not found, invalid parameters, an instance of that name already existing. - Unrecognized errors are retried, but the same one three times in a row aborts the hunt rather than looping forever.
Between passes it waits --delay seconds — a live countdown on a terminal,
a single log line in scripts. The hunt ends with one of: success (the VM
is recorded locally and a summary with the gcloud compute ssh command is
printed); no zone in the selected scope can host this machine/GPU
combination (every candidate zone was skipped — widen or drop --region,
or change the machine or GPU type); no capacity found after N passes when
--max-attempts is set (re-run the same command to keep hunting); or
Ctrl-C (exit 130, with a warning if a create call was in flight and may
have produced an instance server-side).
The lifecycle commands take no targeting flags: they operate on the single VM
recorded by the last successful create (managing one VM at a time is an
explicit v1 scope decision).
gce-hunter stop # warns that a stopped GPU VM may be unable to restart
gce-hunter start # starts it again — or offers disk recovery (see below)
gce-hunter status # recorded state (and its file path) + live status and external IP
gce-hunter delete # deletes the VM and its boot disk; type its name to confirm-y, --yes (global) skips confirmation prompts — it never fills in missing
values. In scripts delete refuses to run without it, and start refuses to
begin disk recovery without it. If the VM was deleted outside gce-hunter,
status reports it as NOT FOUND and delete just clears the stale state.
If start fails with a capacity error, your VM is intact but its zone has
no room for it right now. gce-hunter then offers to:
- Snapshot the stopped VM's boot disk as
gce-hunter-rec-<timestamp>(the VM is not modified — snapshots are global, so any zone can use it). The exact command to remove the snapshot by hand is printed right away. - Hunt a replacement VM booted from that snapshot, with the same
machine type and GPU as the stopped VM (a different one could be
incompatible with what is installed on the disk). You are asked for the
region filter, prefilled with the recorded one: press Enter to keep it,
shorten it to widen the hunt — the typical move, the original zone may
stay dry for a while — or delete it to hunt worldwide (
startaccepts-n/--max-attemptsand-d/--delayfor this hunt; scripts reuse the recorded filter silently). - Only after the replacement VM is RUNNING and recorded: delete the old VM (its boot disk auto-deletes with it) and the snapshot.
The old VM and its disk are never touched before the replacement is up. On
any failure or Ctrl-C the old VM stays intact and the snapshot is deleted
best-effort — if that cleanup fails, the exact gcloud command to remove it
is printed. If the state file cannot be written once the replacement is
running, nothing is deleted: both VMs are listed with the commands to
clean up by hand. The replacement gets an incremented name (my-vm →
my-vm-2 → my-vm-3).
On a gcloud too old to accept --source-snapshot when creating an instance,
the hunt switches to pre-creating a boot disk from the snapshot in each zone
it tries (removing it again when the zone fails) and suggests
gcloud components update.
The recorded VM lives in state.json under the platform config directory:
~/.config/gce-hunter/ on Linux and ChromeOS, ~/Library/Application Support/gce-hunter/ on macOS, %APPDATA%\gce-hunter\config\ on Windows.
create writes it, delete removes it, status prints its path. Deleting
the file by hand simply makes gce-hunter forget the VM.
0 success · 1 runtime error · 2 command-line usage error · 130
interrupted with Ctrl-C.
The wagon branch is a specialization of this tool for
learners of Le Wagon's Data Science & AI bootcamp:
same engine, but with curriculum-aligned choices baked in — a fixed GPU
priority order, one pinned image, a fixed disk and naming scheme — so a
learner cannot misconfigure their VM. Learners choose only their project
and an optional region filter, and install with the same one-line installer
(pointed at the wagon branch).
cargo test # unit + integration tests (gcloud is always mocked)
cargo clippy --all-targets -- -D warnings
cargo fmt --checkThe integration tests run the real binary against a scripted mock-gcloud
double; no test ever contacts Google Cloud. CI runs the lints and the tests on
Linux, macOS and Windows. Internals used by the tests: GCE_HUNTER_GCLOUD
(path of the gcloud executable to use — also handy for exotic installations),
GCE_HUNTER_CONFIG_DIR (state-file directory), GCE_HUNTER_DELAY_OVERRIDE,
GCE_HUNTER_ZONE_DELAY_MS, GCE_HUNTER_SEED.
Releasing. Pushing a tag builds the six targets and publishes the
archives plus SHA256SUMS as a GitHub release. Tags are vX.Y.Z on master
and vX.Y.Z-wagon on wagon — both branches share the releases page, and
each branch's install.sh only ever picks tags of its own channel. To test
the installer without a release, serve a fake one locally
(<dir>/<tag>/gce-hunter-<tag>-<target>.tar.gz + SHA256SUMS, e.g. with
python3 -m http.server) and run it with GCE_HUNTER_BASE_URL,
GCE_HUNTER_VERSION and a temporary GCE_HUNTER_INSTALL_DIR.
MIT — see LICENSE.