CH·02CLI reference

gpu instances create

Create a new GPU instance

Create a new GPU instance

Synopsis

Creates a GPU instance asynchronously. The CLI polls the operation FSM until it reaches succeeded (or failed/cancelled), then fetches and prints the booted instance. Use --wait-timeout=0 to skip polling and print just the operation_id for scripts that want to poll asynchronously.

gpu instances create [flags]

Choosing a launch environment

--environment selects the launch environment (see the Certified Image guide for the full picture):

  • certified — launch the GPU.ai Certified Image: the one normalized, digest-pinned environment. Available only on certified-capable machines.
  • certified: — a framework environment: a thin, digest-pinned framework layer on the certified base, with all the same guarantees (driver floors, digest pins, capability gating). Available frameworks:
- certified:pytorch — latest PyTorch + torchvision (CUDA-matched wheels) with JupyterLab - certified:cuda-devel — the CUDA compiler toolchain (nvcc, headers, dev libraries) for building extensions the runtime base can't compile - certified:jupyter — JupyterLab on the lean base
  • certified:@ — a specific framework version (the version picker), e.g. certified:pytorch@2.13. Versions are CUDA-generation-specific: a version only launches on GPUs whose CUDA variant it was built for. A bare certified: uses the default build for the placed machine's CUDA generation.
  • raw-vm — a VM we bootstrap for you with root access, on the provider's default OS. The CLI spelling raw-vm maps to the API wire value raw_vm.
  • raw-vm: — a root VM with a chosen operating system (the OS picker), e.g. raw-vm:ubuntu-24.04. Run gpu environments for the OS catalog.
  • provider-template — launch from the provider's own runtime template (container-native or otherwise non-normalizable capacity). These machines launch without the GPU.ai Certified Image.
Run gpu environments to list every framework, version, and OS you can pass here. Leaving --environment empty uses the provider default.

A framework that isn't published for the selected machine is rejected synchronously with 422 environment_unavailable — *"the selected framework environment isn't available for this machine — pick another framework or the certified base image"*.

Capability gate (422). The environment you pass is re-validated server-side against the availability catalog. If no offering of the requested GPU type can honor the class, the launch is rejected with a 422 environment_unavailable problem+json — never a silent substitution. For example:

  • --environment raw-vm on container-only capacity → *"this machine is container-native — root VM access isn't available"*
  • --environment certified on template-only capacity → *"this provider launches from its own template; the GPU.ai Certified Image isn't available"*
Use gpu pricing --certified (or --type vm) to find machines that can honor the environment you want before launching.

Container-native capacity cannot run Docker-in-Docker. On container machines (TYPE container in gpu pricing) your workload runs *as* an unprivileged container, so a Docker daemon started inside it fails on iptables/network-bridge setup — nested docker run, docker compose, docker build, and --privileged do not work. If your workload runs its own Docker (an inference server you start from a Docker image, for instance), pick VM capacity first with gpu pricing --type vm and launch it with --environment raw-vm, where you get root and Docker-in-Docker works.

Bringing your own image

--image launches the instance from your own container image instead of a template or a certified environment (full guide: Custom images):

gpu instances create -t h100_sxm --image ghcr.io/acme/trainer:v1
  • The image must be pullable — publicly, or privately with --registry-credential (a stored login from gpu registry-credentials) — and provide linux/amd64. Both are verified *before* anything is provisioned: an unknown image (or a wrong login) fails the create with 422 image_unavailable, a registry outage with 502 registry_unreachable — no operation, no billing.
  • The launch is digest-pinned to the exact manifest the verification saw, so a tag re-push between verify and pull can't change what boots.
  • After SSH and tunnel setup, the image's own ENTRYPOINT/CMD is started; its output lands in /var/log/gpuai-entrypoint.log on the instance. An image with neither is a plain SSH environment. Your image does not need to run sshd — the platform provides SSH either way.
  • --entrypoint / --cmd (one argv element per use) override the image's process defaults with docker semantics: a non-empty entrypoint override also drops the image CMD unless --cmd is given. A single --entrypoint "" clears the image's ENTRYPOINT while keeping its CMD (docker-style); a single --cmd "" clears the CMD. Empty elements inside a longer argv are rejected (422).
  • --app-port 8000 serves that HTTP port at https://.apps.gpu.ai behind the instance's basic-auth login (shown by gpu instances get); extra --port N/http or --port N/tcp entries are forwarded to the provider's port exposure. Port 22 is reserved.
  • --env KEY=VALUE (repeatable) sets plain container env vars for the workload. PUBLIC_KEY and GPUAI_* are reserved by the platform and refused.
  • Placement prefers container-native capacity (TYPE container in gpu pricing), where SSH lands inside your container. On VM capacity the platform runs your image as a container beside the host's SSH (docker logs gpuai-workload from the host); private images stay container-native. See where custom images run.
  • Mutually exclusive with --environment — a custom image *is* its own environment.
  • On-demand only for now. --image with --tier spot is refused (422 validation_failed): a spot instance can be reclaimed and re-routed, and the re-route cannot yet carry your image's env, entrypoint, ports, or registry login — so it would boot a stock replacement in its place. Launch custom images with the default on_demand tier.

Pinning an exact offering

--offering-id pins the launch to one exact offering ID (from the offering_id field of gpu pricing --output json). This is an exact pin: if that specific offering is no longer available at launch time, the launch fails with an explicit error — *"selected machine is no longer available — pick another offering"* — and never silently falls back to a different (possibly pricier) offering. Leave --offering-id empty for normal price-sorted placement.

Choosing a disk size

--disk sets the instance filesystem size in GB (10–1000). Every launch gets 100 GB by default — leaving --disk unset costs nothing extra, and placement always avoids machines whose real storage can't fit the size your launch will request, the default included. Disk beyond the included 100 GB is billed at the offering's per-GB hourly rate (disk_price_per_gb_hour in gpu pricing JSON), folded into the instance's single hourly price — the rate shown at create time is the rate billed.

Setting it is a hard requirement, not a hint: placement narrows to capacity that can honor the size (the disk_configurable field in gpu pricing — a trailing + in the DISK (GB) column), and a launch that can't get the disk it asked for fails with a 422 disk_unavailable problem+json — *"the selected machine cannot be configured with a custom disk size — pick another machine or launch with the default disk"* — never a silent launch at a different size.

The size itself is honored the same way. On an unpinned launch, placement only selects hosts whose real storage fits the request — a machine that can't fit it is skipped, never silently clamped. On a pinned launch (--offering-id), asking for more disk than the offering's displayed storage_gb (in gpu pricing JSON) is refused up front with a 422 disk_exceeds_offering problem+json — *"requested disk_gb exceeds the storage this machine can deliver — lower disk_gb or pick another machine"* — rather than launching with a clamped filesystem.

Machines whose DISK (GB) column has no + bring their own fixed disk (typically full-VM capacity); the shown figure is what you get.

Bounding an instance's runtime

--auto-terminate-hours terminates the instance automatically after that many hours of runtime (1–720, i.e. up to 30 days). Omit it — the default — and the instance runs until you or a spending limit stops it.

This is protection for unattended runs. If the script or agent that launched an instance crashes, nothing else is watching the meter: without a limit the instance bills until you notice or your balance runs out. A timer in your own process is no help — it dies with the process. This limit lives on the server, so it fires whether or not anything of yours is still running.

  • The clock starts when the instance reaches running, not when you run this command. Provisioning can take minutes on slow-boot capacity and isn't billed, so it never eats into the hours you asked for.
  • gpu instances get shows the resolved deadline (Auto-terminate: 2026-09-02T18:00:00Z (8h limit)); gpu instances list grows an AUTO-TERM column when any instance in the listing has a limit.
  • When it fires the instance is terminated exactly as gpu instances terminate would terminate it: billing stops, the instance.terminated webhook fires, and Status Reason on the terminated instance records that the runtime limit was reached.
  • It is a hard stop, not a warning — the instance and its local disk are gone. Persist anything you need to keep (checkpoints, outputs) to your own storage before the deadline.
# A training run that must never outlive the working day
gpu instances create -t h100_sxm --auto-terminate-hours 8

This is per-instance and independent of the org-wide auto_terminate_hours on your spending limit, which only acts once a monthly cap has already been breached.

Placement modes & the price cap

Every launch runs in one of two placement modes, and the CLI states which one on every create:

  • Pinned (--offering-id set) — the exact offering you chose, at its listed rate, or an explicit error. Nothing is ever substituted, so the price you saw in gpu pricing is the price you get.
  • Unpinned (no --offering-id) — the cheapest capable capacity matching your filters wins, and the final rate is whatever that placement costs, bounded by the price cap.
--max-price is that cap on the retail $/hr (whole node). It is enforced on every placement attempt — including fallbacks when the cheapest offering ghosts, and any boot-rescue reprovision later — so a launch is never billed above it. The trade-off is real: capacity priced above the cap is skipped, which shrinks the pool placement (and a rescue) can draw from.

If you don't pass --max-price on an unpinned launch, the CLI defaults the cap to the cheapest currently-listed price matching your filters — so by default, the price you see quoted at create time is the most you can be billed. Pass --max-price 0 explicitly to launch uncapped ("just give me the market price"); the final rate is then set at placement and returned as price_per_hour on the created instance.

If the cheapest available capacity exceeds the cap at placement time, the launch fails with the current best price so you can re-run to accept it or raise --max-price — never a silent launch above your cap. Even on an uncapped launch, a boot rescue can never re-bind the instance above the rate that was originally placed.

Holding the price you decided on

A pin locks the machine, not the rate. A catalog row is a quote, not a booking: on marketplace supply the row can reprice between the moment you read gpu pricing and the moment your launch resolves, and by default that new price is simply what you're billed.

--viewed-price closes that gap. Pass the retail $/hr you decided to launch at — normally the price_per_hour from the same gpu pricing row you're pinning — and if the launch-time price has moved more than a cent above it, the launch fails with price_changed carrying the new price instead of proceeding at it. Re-run with the new number to accept it. The one-cent tolerance absorbs a rounding difference between the quoted and billed folds of a priced disk, not a real price move.

It pairs with --offering-id; the tolerance also applies to fallback capacity on an unpinned launch, so nothing is ever placed at a price you didn't pass. It is not a spending cap — it can only refuse a launch, never authorize a pricier one, and it never raises what you're charged. Use --max-price to bound spend.

Omitting it accepts the launch-time price, which is the previous behavior — existing scripts are unaffected.

# Pin the machine AND the price you read off gpu pricing
gpu instances create -t h100_sxm --offering-id off_abc123 --viewed-price 2.85

Examples

# Launch a single H100 SXM on-demand
gpu instances create -t h100_sxm --tier on_demand

# Pin to a region and inject an SSH key
gpu instances create -t a100_pcie --region us-east --ssh-key-id key-abc123

# Don't wait — just submit and print operation_id
gpu instances create -t h100_sxm --wait-timeout 0

# Multi-GPU instance with a custom name
gpu instances create -t h100_sxm --count 4 --name training-rig

# Launch the GPU.ai Certified Image (fails 422 if the machine can't run it)
gpu instances create -t rtx_pro_6000_blackwell --environment certified

# Launch your own public image, with env vars for its entrypoint
gpu instances create -t h100_sxm --image ghcr.io/acme/trainer:v1 --env MODEL=acme/llm --env PRECISION=bf16

# Launch the PyTorch framework environment on the certified base
gpu instances create -t h100_sxm --environment certified:pytorch

# CUDA devel — nvcc + headers for building extensions
gpu instances create -t h100_sxm --environment certified:cuda-devel

# Raw VM with root access
gpu instances create -t h100_sxm --environment raw-vm

# Ask for a 200 GB filesystem (fails 422 disk_unavailable if nothing can honor it)
gpu instances create -t h100_sxm --disk 200

# Bound an unattended run: terminate 8 hours after it starts running
gpu instances create -t h100_sxm --auto-terminate-hours 8

# Pin one exact offering (fails if it's gone — never substitutes)
gpu instances create -t h100_sxm --offering-id off_abc123

# Cap the price: place at or below $2.50/hr, never above (fails with the current price if the cheapest is over)
gpu instances create -t h100_sxm --max-price 2.50

# Uncapped: take the market price, whatever placement costs (rate returned on the instance)
gpu instances create -t h100_sxm --max-price 0

Options

      --count int                 Number of GPUs (default 1)
      --disk int                  Instance filesystem size in GB (10-1000). 0 = the platform default (100 GB); placement always avoids machines whose storage cannot fit the size the launch will request, default included. Disk beyond the included 100 GB is billed at the offering's disk_price_per_gb_hour (see gpu pricing), folded into the hourly rate. Setting it narrows to capacity that can honor it; an unhonorable request fails rather than launching with a different size
      --environment string        Launch environment: certified (GPU.ai Certified Image) | certified:<framework> (pytorch, cuda-devel, jupyter — a framework layer on the certified base) | raw-vm (root VM access) | provider-template; empty = provider default
      --auto-terminate-hours int  Terminate this instance automatically after N hours of runtime (1-720; 0 = no limit, the default). The clock starts when the instance reaches running, not when this command runs — provisioning is neither billed nor counted — and `gpu instances get` shows the resulting deadline. Enforced by the platform, not by this CLI: it still fires if this process is gone, which is the point for unattended runs
      --app-port int              HTTP port served at your instance's app URL (https://<id>.apps.gpu.ai, basic-auth protected). Added to --port automatically
      --cmd stringArray           Override the image CMD; one argv element per use
      --entrypoint stringArray    Override the image ENTRYPOINT; one argv element per use. Replaces the image CMD too unless --cmd is given
      --env stringToString        Container env var KEY=VALUE for a --image launch (may repeat); PUBLIC_KEY and GPUAI_* are reserved
  -h, --help                      help for create
      --image string              Launch from your own container image (e.g. ghcr.io/acme/trainer:v1). Must be pullable (public, or private with --registry-credential) and linux/amd64 — verified before anything is provisioned, and the launch is pinned to the exact digest verified. The image's own ENTRYPOINT/CMD starts after SSH setup. Mutually exclusive with --environment
      --port stringArray          Expose a container port as PORT/PROTOCOL (http|tcp), e.g. --port 8000/http (may repeat)
      --registry-credential string   Stored registry-credential ID for pulling a PRIVATE --image (see gpu registry-credentials)
      --max-price float           Cap the retail $/hr (whole node); placement and any boot rescue never exceed it. On an unpinned launch the cap DEFAULTS to the cheapest currently-listed matching price; pass 0 explicitly to launch uncapped. Fails with the current price if the cheapest available is above the cap
      --name string               Optional instance name
      --offering-id string        Pin the launch to one exact offering ID; empty = price-sorted placement
      --region string             Region (e.g., us-east); empty = any
      --ssh-key-id strings        SSH key ID to inject (may repeat)
      --tier string               Tier: on_demand (spot capacity is currently unavailable sitewide) (default "on_demand")
  -t, --type string               GPU type ID (e.g., h100_sxm) — required
      --viewed-price float        The retail $/hr you decided to launch at (pair with --offering-id). A pin locks the machine, not the rate: if the price has moved more than a cent above this by launch time the launch fails with the new price instead of proceeding at it. 0 = don't send one, which accepts the launch-time price (the default, unchanged behavior). This is NOT a spending cap — it can only refuse a launch, never authorize a pricier one; use --max-price for that
      --wait-timeout duration     How long to poll for status=running before printing the operation_id (default 5m0s)

Options inherited from parent commands

      --api-base string   API base URL (override with GPUAI_API_BASE env) (default "https://api.gpu.ai/v1")
      --debug             Enable debug logging to stderr
  -o, --output string     Output format: table|json (default table on TTY, json otherwise)

SEE ALSO

← The gpu CLI