Skip to main content

System images

NixOS images are built with Nix Flakes and Ubuntu images with Ansible Playbooks GPU sizes auto-select the appropriate GPU base image (gpu-h100x1-base, gpu-h100x8-base, or gpu-amd-base). The GPU base images only work with GPU sizes. For NixOS on GPUs, pass --image nixos-25-11-nvidia explicitly — GPU sizes still auto-select the Ubuntu-based gpu-*-base images by default. The image ships the NVIDIA driver stack, CUDA, and the container toolkit pre-installed (docker --gpus all works out of the box), and the SSH user is nix. It runs on the supported NVIDIA GPU sizes: gpu-4000ada-1, gpu-6000ada-1, and gpu-h100-1. AMD sizes (gpu-mi300x-*) are not supported. GPU sizes are region-limited — see Pricing. Like the other GPU base images, it is hidden from machine0 images ls by default — use machine0 images ls --all. Snapshots you take of it behave like other GPU snapshots (next paragraph): they stay GPU-compatible and can be parked on a CPU size with enough disk, but the NVIDIA stack boots degraded off GPU hardware, so treat CPU sizes as parking rather than a place to run the workload. Snapshots you take of a GPU VM are regular images with the GPU drivers baked in — you can use them with any size that has enough disk (a 1x-GPU snapshot needs 5xl or 6xl), and snapshots of those VMs stay GPU-compatible, so you can move the work back to a GPU size later. This makes it cheap to park GPU work: snapshot the GPU VM, destroy it, and pay only for snapshot storage until you need it again. GPU and standard VMs live in different datacenters within the same region. If a snapshot isn’t yet in the datacenter your chosen size needs, machine0 new starts the transfer automatically and asks you to retry — it usually takes a few minutes (check progress with machine0 images get <image>).

Custom images

Snapshot any VM into a reusable image:
Set a custom image as default:
Rename, redescribe, or add regions to an image from machine0 images update, the MCP images tools, or the Update action on the web dashboard’s Images page. On the dashboard you can also select multiple images and destroy or transfer (add regions to) them in one step — up to 100 at a time, with per-image success/failure reporting. System images can’t be bulk-selected.

Versioning

Images support multiple versions. Snapshot the same image name again to create a new version. Promote any version to be the active one. See CLI reference and MCP reference.

Branch tracking

Images built from a GitHub flake (machine0 images new <image> --flake 'github:owner/repo#host') track their branch: the repo, branch, directory, and nix attribute are recorded on the image (the Git Repo / Git Branch rows in machine0 images get), and every push to that branch automatically builds a new version and promotes it once the build succeeds. Each version keeps the exact commit it was built from (Built From in machine0 images versions get).
  • The tracked branch is the ?ref= in your flake reference, or the repo’s default branch when you don’t pass one — the resolved branch name is stored either way.
  • A pinned ?rev= needs its branch spelled out: ?rev=<sha>&ref=<branch> (rev pins this build, ref is what pushes rebuild from). ?rev= alone is rejected.
  • Automatic rebuilds need the machine0 GitHub App installed on the repo (dashboard → Account → GitHub) — that’s what delivers the push events, and rebuild admission verifies that YOUR installation covers the repo before building (a push from a repo your installation doesn’t cover is ignored). The CLI warns at build time when the repo isn’t covered. Public repos still build fine without it; they just won’t rebuild on push.
  • Rebuilds run as you: they use your builder defaults, bill like any build, and respect your balance. A push that arrives while a build is already running is remembered and picked up when the build finishes; one parked behind a draft you created and left unpromoted is picked up within the hour once you promote or remove that draft.
  • Pushing a branch deletion does nothing — the last built version stays.
  • Saving a VM snapshot (or a local-folder build) onto a tracked image turns tracking off: the image’s content no longer comes from the repo, and a later push must not silently replace it. Re-track by running another --flake github:… build.
  • Automatic rebuild promotion never downgrades: if you manually promote a newer version while an automatic build is still running, the automatic one completes as a draft and is not promoted over yours.
  • Turn tracking off any time with machine0 images update <image> --no-track — pushes stop rebuilding immediately and automatic builds that haven’t started promoting finish as plain drafts; re-track by running another --flake github:… build.

Private flake inputs

Builds are anonymous-first: when the tracked repo is publicly reachable, your GitHub credentials are never placed on the builder — a third-party public flake’s build scripts must not be able to read your token. The flip side: a public flake whose lock file pins private inputs (github: or git+https://) builds unauthenticated and fails on those inputs with the nix fetch error (see Failed builds below for where to read it). To build with credentials, make the top-level repo private — a private tracked repo resolves through your GitHub connection — or vendor the private inputs into the repo.

Failed builds

A failed build keeps its draft version — with the full nixos-rebuild log tail — for 7 days, so the error stays readable: machine0 images versions get <image> <version> prints the whole error block (the fatal nix error is at the end of the log), the dashboard image panel shows the latest failure, and the events feed carries an excerpt. The failed draft never blocks new work: a retry, a new push, or a snapshot onto the image supersedes it immediately, and so does an upload when the image is ready (an image whose very first build failed is errored, not ready — supersede it with a retry, push, or snapshot; promoting the superseding version returns the image to ready). Build errors are visible to the image owner and image admins only.

Environment variables and snapshots

Environment variables injected via --profile (profile variables) are written to disk files (/etc/environment and /etc/profile.d/machine0-env.sh). When you snapshot a VM with images new, these files are captured in the image. Any secrets stored in profile variables will persist in the resulting snapshot. Rotate secrets after saving an image if they should not be reused. This includes the auto-injected MACHINE0_API_KEY: VMs created from the image without a profile scrub the env files on first boot, but the key bytes remain inside the image itself (same as ~/.machine0/auth-token below).The same applies to credentials injected by --profile: ~/.config/gh/hosts.yml, ~/.codex/auth.json, ~/.codex/config.toml, ~/.claude/.credentials.json, ~/.claude/settings.json, ~/.claude.json, and ~/.machine0/auth-token are on-disk files and travel with snapshots. A codex account credential in particular self-refreshes, so an image baked from a codex-authenticated VM stays signed in on every VM created from it — log out of codex on the VM before snapshotting if that’s not what you want. Integrations connected with an API key bake that key into the snapshot too, and the key stays valid until you revoke it in the provider’s console — treat images made from API-key-authenticated VMs as carrying the key.

SSH client initial command and snapshots

If the source VM has an SSH client initial command set, it is copied onto the snapshot and inherited by VMs created from that snapshot (override it any time with machine0 update). Clearing the command on a machine does not remove it from snapshots taken earlier, and pre-existing snapshots are not retroactively updated. The value is stored and displayed in plain text — don’t embed secrets in it; use profile variables for secrets. System images never carry an initial command.

Billing

$0.078/GB/month based on disk size, billed hourly at the actual prorated cost (no minimum). See Pricing.