Exclusive Access · Invitation Only

Creating Virtual Machines

A virtual machine is made from a registered image: the node gives it CPUs, memory, its own disk layered on the image, and one to four network interfaces, configures its first boot, and starts it.

Part of Compute, in early access.

Create one

sudo cenvero-str-ctl vm create --name web-01 --tenant t-acme --image img-3f9a1c2e \
  --vcpus 2 --memory 2048 --disk 20 \
  --network net-acme --ssh-key-file ~/.ssh/id_ed25519.pub
FlagMeaningDefault
--nameLowercase letters, digits and hyphens, 1–63 characters, not starting or ending with a hyphen. Unique within its tenant on this noderequired
--imageThe base image's idrequired
--vcpusVirtual CPUs: from 1 up to the node's CPU count1
--memoryMemory in MiB: at least 128, at most the node's memory (machines together may add up to more)1024
--diskDisk size in GiB: at least the image's own size, at most 65536the image's size
--network NET or NET=IPAttach to a managed network, at its next free address or the one you name. Repeatable—
--public-ip auto or IPAn interface holding a public address your provider routes to this server. Repeatable—
--tenantThe customer the machine belongs tonone (an operator machine)
--hostnameThe guest's hostnamethe name
--ssh-key-fileA public key file to authorize (every non-comment line is a key). Repeatable, up to 32 keys—
--user-data-fileYour own cloud-init user-data—
--dnsA DNS server for the guest. Repeatable, up to 3the node's upstream resolvers
--bandwidth-mbpsA bandwidth limit on each of the machine's interfacesnone
--no-startCreate it stoppedstarts it

The firmware (--firmware uefi, --secure-boot, --tpm) and the room a machine has to grow while it runs (--max-vcpus, --max-memory) are chosen at create too: see Firmware, resizing, interfaces and the guest agent.

A machine needs at least one --network or --public-ip, and has at most four interfaces in all. Public interfaces come first, so the guest's first interface is the one it is reachable on. How each kind is wired is on Networks and public addresses.

The node also needs at least 1 GiB free in its disk pool.

A create completes, or leaves nothing behind. A request that cannot be met — an image that is not ready, a network of another tenant, an address in use, a quota reached — is refused before anything is made, and a failure half-way is undone. If the machine is created but will not start, it is kept, stopped, with the reason in state_reason; fix the cause and run vm start.

First boot

The machine's first boot is configured through cloud-init: the node attaches a small read-only disk labelled cidata with three files, rebuilt from its records every time the machine starts.

  • meta-data: the machine's instance id, its hostname and the SSH keys you gave.
  • network-config: one entry per interface, matched by its MAC address, with the interface's static address. The default route goes through the first public interface when there is one (see public addresses); otherwise through the first interface's network gateway, if that network has one. The first interface also carries the DNS servers.
  • user-data: your file, passed on exactly as you wrote it. It must start with #cloud-config or #! and be at most 64 KiB. Without one, the node sends only what sets the hostname.

cloud-init applies these once, on the machine's first boot; what the guest changes afterwards is its own. SSH keys and user-data are kept on the node only, readable by root; vm show reports how many keys and how many bytes of user-data a machine has, never their content.

#cloud-config
packages: [nginx]
runcmd:
  - systemctl enable --now nginx
sudo cenvero-str-ctl vm create --name www --tenant t-acme --image img-3f9a1c2e \
  --vcpus 2 --memory 2048 --network net-acme --user-data-file ./www.yaml

Tenants

A machine with --tenant belongs to that customer:

  • it can attach only to that tenant's networks (and an operator machine, with no tenant, only to networks without one);
  • it is kept apart from every other tenant's machines and containers, on its own port, before it first runs (see Tenants);
  • it cannot be created or started while the tenant is suspended, and a suspension stops it (the resume starts it again if it was running — see Lifecycle);
  • the tenant cannot be deleted while it has machines.

To cap how many machines a tenant may have on this node:

sudo cenvero-str-ctl tenant quota-set t-acme --max-vms 10
cenvero-str-ctl tenant quota t-acme        # max_vms and used_vms

0 means no cap. Lowering the cap never touches machines that already exist; it only refuses the next create.

Bandwidth. --bandwidth-mbps limits each of the machine's interfaces. It is refused for a tenant that already has a bandwidth cap: the tenant's cap applies to its machines (see Tenants & Bandwidth).

Looking at machines

cenvero-str-ctl vm list
cenvero-str-ctl vm list --tenant t-acme
cenvero-str-ctl vm show vm-3f9a1c2e
{
  "vm": {
    "id": "vm-3f9a1c2e",
    "name": "web-01",
    "tenant_id": "t-acme",
    "state": "running",
    "desired_state": "running",
    "state_reason": "",
    "flags": [],
    "vcpus": 2,
    "memory_mib": 2048,
    "disk_gib": 20,
    "image_id": "img-3f9a1c2e",
    "hostname": "web-01",
    "hypervisor": "kvm",
    "nics": [
      { "index": 0, "network_id": "net-acme", "ip": "10.30.0.20", "mac": "02:ce:0a:1e:00:14",
        "interface": "cnv-v-3f9a1c2e0", "bandwidth_mbps": 0 }
    ],
    "user_data_bytes": 0,
    "ssh_key_count": 1,
    "console_sessions": [],
    "created_at": "2026-09-28T09:20:11Z",
    "updated_at": "2026-09-28T09:21:02Z"
  }
}

state is what the machine is doing, desired_state what you last asked for; Lifecycle and restarts explains the states and the flags. hypervisor is kvm, or emulated on a node without hardware virtualization.

Changing a machine

CPUs and memory are changed with vm update, live on a running machine within its maximums; interfaces are added and removed with vm nic add and vm nic remove, also while it runs. Both are on Firmware, resizing, interfaces and the guest agent, with the guest agent (setting a password, the guest's own addresses).

Growing the disk

sudo cenvero-str-ctl vm stop vm-3f9a1c2e
sudo cenvero-str-ctl vm resize vm-3f9a1c2e --disk 40
sudo cenvero-str-ctl vm start vm-3f9a1c2e

The machine must be stopped, and a disk can only grow. The guest sees the new size on its next boot; most cloud images then grow their root file system to fit by themselves.

Deleting

sudo cenvero-str-ctl vm delete vm-3f9a1c2e --yes

A running machine is powered off at once (not shut down gracefully — stop it first if the guest should shut down cleanly), and its disk, its first-boot disk and its interfaces are removed. Its network addresses and public address are free again straight away. The disk is gone for good — there are no snapshots or backups of machine disks yet — and its space is released without being overwritten first. A delete that is interrupted can be run again; it carries on where it stopped.

Over the API

curl -k -X POST "$NODE/api/v1/vms" -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{
    "name": "web-01", "tenant_id": "t-acme", "image_id": "img-3f9a1c2e",
    "vcpus": 2, "memory_mib": 2048, "disk_gib": 20,
    "networks": [ { "network_id": "net-acme", "ip": "10.30.0.20" } ],
    "ssh_authorized_keys": [ "ssh-ed25519 AAAA… [email protected]" ],
    "user_data": "#cloud-config\npackages: [nginx]\n",
    "dns": [ "192.0.2.53" ],
    "start": true
  }'
MethodPathNotes
GET/api/v1/vms?tenant_id= for one tenant's
POST/api/v1/vms201 with the machine. Each networks entry has network_id (optionally ip) or public_ip, and optionally bandwidth_mbps
GET/api/v1/vms/{id}One machine
DELETE/api/v1/vms/{id}Delete it and its disk
POST/api/v1/vms/{id}/resize{"disk_gib": 40}, stopped machines only

Changing CPUs, memory and interfaces, and the guest agent, have their calls on Firmware, resizing, interfaces and the guest agent.

Every change answers as described here and also names the task that records it ("task", and a Location header): who asked, what was done and how it ended — including a create that failed and was rolled back. A machine's history is GET /api/v1/tasks?object=vm-3f9a1c2e; see Tasks. GET /api/v1/resources lists every machine with its state in one answer.

Starting, stopping and restarting are on Lifecycle and restarts. Errors come back as {"error": "…"} in plain words: 400 for a request that is not valid, 404 for an unknown machine or image, 409 for a conflict (a name in use, a state that does not allow it, a quota reached), 503 while Compute is not ready, 403 when the licence does not allow it. Unknown fields in the body are refused, not ignored.

Tenant-scoped API keys cannot manage machines yet; use an operator token or key.

See also

↓ This page as JSON ↓ All documentation as JSON