Create an instance
This guide creates a running instance from an existing image with tritonctl,
the tenant CLI. You end with an instance record you can list, show, stop, and
delete.
You need four things before you start:
tritonctlauthenticated against your endpoint (tritonctl configure).- The UUID of the project the instance belongs to.
- The UUID of the boot image.
- The UUID of the subnet the primary NIC attaches to.
Every selector in tritonctl is a UUID. No command accepts a resource name.
tritonctl instance create sends only ?project= on POST /v1/instances, and
that handler requires both ?tenant= and ?project=. The command returns
400 MissingScope until the CLI is fixed. Use the API call in
Create the instance through the API
to get an instance today.
Register an SSH key
Keys registered this way are owned by the calling user. There is no tenant, silo, or project selector on this command.
On your workstation:
tritonctl ssh-key create --name workstation --public-key-file ~/.ssh/id_ed25519.pub
The command prints Registered ssh key followed by the new key's UUID, then the
key's id, name, scope, and fingerprint. Record the UUID: you pass it to
instance create as --ssh-key-id.
--public-key takes the OpenSSH key string inline instead of a path. The two
flags conflict; passing neither fails with
--public-key or --public-key-file is required.
The server parses the key as OpenSSH and rejects anything it cannot parse with
invalid openssh public key, followed by the parser's own message.
To list the keys you already have:
tritonctl ssh-key list
The table columns are ID, NAME, FINGERPRINT, and SCOPE.
Find the image and subnet UUIDs
Read one image by UUID:
tritonctl image show <IMAGE_UUID>
The command prints id, name, os, version, size in bytes, and sha256.
tritonctl image list sends scope=tenant, and GET /v1/images implements
scope=public only, so that command returns 400 ScopeNotImplemented. Get the
image UUID from your operator until the tenant scope is implemented.
List the subnets in a VPC:
tritonctl subnet list --vpc <VPC_UUID>
The table columns are ID, NAME, and VPC. --vpc is required: the server
rejects an unscoped subnet list with
GET /v1/subnets requires `?vpc=<uuid>` .
tritonctl vpc list sends only ?project= against a handler that requires
?tenant= and ?project=, so it returns 400 MissingScope. Get the VPC UUID
from your operator.
The subnet must live in a VPC inside the same project as the instance. A subnet
from another project resolves as 404 NotFound.
Create the instance
On your workstation:
tritonctl --project <PROJECT_UUID> instance create \
--name web-1 \
--image-id <IMAGE_UUID> \
--primary-subnet-id <SUBNET_UUID> \
--ssh-key-id <SSH_KEY_UUID> \
--cpu 2 \
--memory-bytes 4294967296
--project is a global flag, so it can go anywhere on the line. Without it the
command fails with --project is required to create an instance before any
request is sent.
| Flag | Required | Value | Notes |
|---|---|---|---|
--name | Yes | String | 1 to 63 bytes. No / or \, no ASCII control bytes, no leading or trailing whitespace. Must be unique within the project. |
--image-id | Yes | UUID | Boot image. Must be visible to you. |
--primary-subnet-id | Yes | UUID | Subnet for the primary NIC. Must be in a VPC in the same project. |
--cpu | Yes | Integer | vCPU count. Zero is rejected. |
--memory-bytes | Yes | Integer | Memory budget in bytes. Zero is rejected. |
--description | No | String | Free text. |
--ssh-key-id | No | UUID | SSH key to inject at first boot. Repeat the flag for more than one key. |
--disk-bytes | No | Integer | Boot disk size in bytes. See below. |
The CLI hardcodes extra_nics to empty, mac to none, and user_data to none.
There is no flag for cloud-init user data, an extra NIC, or a pinned MAC, even
though the API accepts all three.
How the boot disk is sized
With --disk-bytes omitted, the boot disk is the image's content size, except
for images whose compatibility block names the bhyve brand, where the floor is
20 GiB.
With --disk-bytes set, the value is floored at the image's content size (a
volume smaller than the image cannot hold it) and capped at 16 TiB at the API
edge.
What the request waits for
The create request runs the instance-create saga and does not return until the
provisioning job reaches a terminal state. The response carries the instance
record re-read after provisioning, so its lifecycle field is the instance's
current state, not the pending it was created in.
The wait is bounded at 600 seconds. On timeout, or on a failed provisioning job, the saga unwinds: the instance record, its NICs, its IPs, and its disks are removed, and the request returns an error.
Create errors
| Response | Cause |
|---|---|
400 MissingScope, POST /v1/instances requires `?tenant=<uuid>&project=<uuid>` selectors | The CLI does not send tenant=. See the API workaround below. |
400 BadRequest, cpu must be greater than zero | --cpu 0. |
400 BadRequest, memory_bytes must be greater than zero | --memory-bytes 0. |
400 BadRequest, disk_bytes must be greater than zero and at most 16 TiB | --disk-bytes is zero or above 16 TiB. |
404 NotFound | The image, an SSH key, the subnet, or the project does not exist or is not visible to you. |
409 Conflict, instance with name "..." already exists in project ... | Another instance in the project already has that --name. |
409 Conflict, a validation message naming the instance field | --name broke one of the name rules in the flag table. |
503, no eligible compute node for placement; capacity, traits, affinity, or scope pinning rejected every candidate | Placement found no compute node that could host the instance. |
Create the instance through the API
This is the request the CLI should be sending. Run it until
tritonctl instance create sends both selectors.
Read your tenant UUID from the tenant_id claim on your token. On your
workstation:
tritonctl whoami
tenant_id is printed only for an identityd token. An API key prints
auth: API key and no claims.
Load the endpoint and access token into the shell:
eval "$(tritonctl env)"
Then post the instance:
curl -sS -X POST \
-H "Authorization: Bearer $TRITONCTL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
"$TRITONCTL_ENDPOINT/v1/instances?tenant=<TENANT_UUID>&project=<PROJECT_UUID>" \
-d '{
"name": "web-1",
"image_id": "<IMAGE_UUID>",
"primary_subnet_id": "<SUBNET_UUID>",
"ssh_key_ids": ["<SSH_KEY_UUID>"],
"cpu": 2,
"memory_bytes": 4294967296
}'
name, image_id, primary_subnet_id, cpu, and memory_bytes are required.
description, ssh_key_ids, disk_bytes, mac, extra_nics, and user_data
default when omitted. The response is 201 with the instance record as JSON.
The silo selector is rejected on this route with 400 ScopeNotAccepted.
Confirm the instance
List the instances in the project:
tritonctl --project <PROJECT_UUID> instance list
The table columns are ID, NAME, STATE, and IMAGE. --project is not
optional here either: with neither --project nor --image, the server returns
400 MissingScope.
Narrow by lifecycle state with --state, which is matched case-insensitively
against pending, provisioning, running, stopping, stopped, and
failed.
STATE carries the lifecycle in its wire form, which is a JSON object keyed by
state rather than a bare string:
{"state":"running"}
Show one instance:
tritonctl instance show <INSTANCE_UUID>
The command prints id, name, lifecycle, and image. Pass -o json for
the full record. JSON is the stable interface; the table layout is not.
The instance record carries no IP address. tritonctl has no command that
prints the primary NIC's address.
Next steps
- Every flag on every instance subcommand:
tritonctl instance. - Authentication, config file, environment variables, and output formats: CLI reference.
- VPC, subnet, floating IP, and firewall commands: Networking commands.
- How VPCs, subnets, and instance NICs relate: Networking.