This is the catalog for the Plain VMs surface. For end-to-end walkthroughs, see the Tutorial.
Tiers per region
Plain VM tiers share the conservative-floor numbers from the Agents cells. US small reports 3 vCPU — the US region provisions one extra — and the rest match the table below.
| Tier | Region | vCPU | RAM | Disk |
|---|---|---|---|---|
| small | US | 3 | 4 GB | 80 GB |
| small | EU | 2 | 4 GB | 80 GB |
| medium | US | 4 | 8 GB | 160 GB |
| medium | EU | 4 | 8 GB | 160 GB |
| large | US | 8 | 16 GB | 240 GB |
| large | EU | 8 | 16 GB | 320 GB |
OS images
Default user is the cloud-image distro default — the create-success view surfaces it so you do not have to guess.
| Image key | Display name | Default user |
|---|---|---|
| ubuntu-24.04 | Ubuntu 24.04 LTS | ubuntu |
| ubuntu-22.04 | Ubuntu 22.04 LTS | ubuntu |
| debian-13 | Debian 13 | debian |
| debian-12 | Debian 12 | debian |
| fedora-43 | Fedora 43 | root |
| rocky-10 | Rocky Linux 10 | root |
| alma-10 | AlmaLinux 10 | root |
Firewall rule shape
Each VM has a Hetzner Cloud Firewall attached. Rules are customer-editable from the dashboard. Schema:
{
"direction": "in",
"protocol": "tcp",
"port": "22",
"sourceIps": ["0.0.0.0/0"],
"description": "SSH"
}
direction—inonly at v1.protocol—tcporudp.port— a port number, or a port range like8000-8100.sourceIps— array of CIDR strings (IPv4 or IPv6).description— free-form label.
Vault entry shape
The Vault stores OpenSSH public keys per account:
{
"id": "<uuid>",
"label": "laptop-2026",
"type": "ssh-ed25519",
"fingerprintSha256": "SHA256:…",
"createdAt": "2026-05-01T12:00:00Z"
}
The Vault accepts ssh-rsa, ssh-ed25519, ecdsa-sha2-nistp256/384/521, and the hardware-key types sk-ssh-ed25519@openssh.com and sk-ecdsa-sha2-nistp256@openssh.com. v1 stores public keys only.
API endpoints
POST /api/plain-vms
Provision a new Plain VM. Returns immediately with status provisioning; the VM is created in the background.
Request body (the VM's hostname is set from name):
{
"name": "box-1",
"tier": "small",
"region": "us",
"osImageKey": "ubuntu-24.04",
"billingType": "metered",
"access": {
"passwordEnabled": false,
"vaultEntryIds": ["<uuid>"]
}
}
POST /api/plain-vms/[id]/start and /stop
Start or stop the VM. Returns 200.
POST /api/plain-vms/[id]/restart
Power-cycle a running VM (stop, then start). The VM must be running; any other state returns 409. Returns 200; the dashboard polls for the VM to return to running. Dashboard path: the Restart button on the VM's page or its list card.
PATCH /api/plain-vms/[id]/billing
Switch the VM between pay-as-you-go and subscribe-and-save. Body: { "billingType": "metered" } or { "billingType": "subscription" }. The VM must be running or stopped. Switching to pay-as-you-go frees the monthly slot it held, but keeps it on your subscription (remove it from the billing page — see Account & billing). Dashboard path: the VM's settings page → Billing type.
POST /api/plain-vms/[id]/transfer
Transfer the VM to another account you have membership on. Body: { "targetAccountId": "<uuid>" }. The VM must be running or stopped (any other state returns 400); the target must be an organization with active billing. The VM lands on the destination as pay-as-you-go regardless of its source billing type. The server keeps running through the transfer.
The VM's SSH keys and root password are baked in at provision time and do not rotate on transfer — whoever holds the credentials keeps OS access. The response includes a transferNote saying so. Rebuild the VM if you need to cut off prior access. Dashboard path: the VM's settings page → Transfer (the confirm dialog repeats this caveat).
DELETE /api/plain-vms/[id]
Destroy the VM. Returns 200; billing closes and the server is torn down in the background. A subscribe-and-save VM's monthly slot stays on your subscription for reuse (remove it from the billing page); a pay-as-you-go VM's charges stop at the destroy timestamp.
PUT /api/plain-vms/[id]/firewall
Replace all firewall rules for the VM. Body is { "rules": [ … ] } using the rule shape above (full-replace — pass the complete desired set). Returns the canonical post-mutation ruleset, re-fetched from Hetzner.
POST /api/vault
Add a public key to the account's Vault. Body: { "type": "ssh_key", "label": "string", "value": "ssh-… …" }.
DELETE /api/vault/[id]
Delete a vault key. Returns 204.
CLI
There is no CLI for Plain VMs at v1. SSH is the operating interface. Future versions may add crabglamp vm ….