compute.sa-saopaulo-1.basaltic.sh. An instance,
its image, its keypairs and its pools all live in one region and never cross
into another.
Launch an instance
Flavors, images, interfaces, disks, keys and cloud-init — and the two
fields whose absence quietly costs you a network.
Lifecycle
What start, stop, reboot, resize and reinstall each preserve, and what
they destroy.
Disks and interfaces
Hot-plugging volumes and NICs, and which of them survive the instance.
Console access
The boot transcript, a screenshot, and an interactive serial session for
when the network is gone.
Launching an instance
202 with the instance in pending. Building the boot
disk, the interfaces and the seed happens after the response, so poll
GET /v1/instances/{instance_id} and watch vm_state.
Sizing: flavors
A flavor is CPU and memory, and nothing else. It carries no disk size — the boot disk is a volume sized at launch — soGET /v1/flavors is a catalog
of compute shapes, not of machine types.
shared | dedicated
shared oversubscribes CPU for higher density. dedicated pins each vCPU
1:1 to a physical core. Both classes run on the same hosts; the class decides
how the instance draws on a host’s threads.general | loadbalancer | database
Only
general flavors can run instances and instance pools. The other two
are reserved for the managed products, whose nodes are operated and priced
by the platform, and are refused here. Pass ?family=general when listing.percent of one vCPU
The guaranteed floor and the ceiling. The instance is entitled to
vcpus × cpu_baseline_pct / 100 cores however busy its neighbours get, and
may not exceed the burst ceiling even on an idle host. Absent means the
flavor guarantees no floor, or imposes no ceiling beyond the vCPU count.aggregate throughput
The instance’s network ceiling, in megabits/s, across all its interfaces.
Absent means uncapped. A resize moves this with the flavor.
GET /v1/flavors is not
paginated.
Choosing an image
image_id takes three forms, and the difference matters for reproducibility:
- A bare name
- name:version
- An image id
"image_id": "debian-13" follows the tag. You get whichever build is
current at the moment the instance is created, so two launches a month
apart can boot different bits.amd64 architecture. See Images and keypairs
for how a tag moves and what the listing does and does not show you.
The boot disk
The boot volume is cloned from the image and sized byboot_volume_size_gb,
which defaults to the image’s min_disk_gb and may not go below it. Anything
smaller is a 400 before the instance row exists, not a failed build.
boot_volume_type picks the tier — ssd or nvme — and defaults to the
region’s. Both fields accept 1–16384 GB.
data_volumes creates blank volumes alongside the instance. Each takes a
size_gb, an optional volume_type, and an optional mount_path — with a
mount path set, the in-guest agent formats the disk (only if it is blank) and
mounts it there, using fstype, default ext4. These default to
delete_on_termination: true, the opposite of a volume you attach later.
SSH keys and cloud-init
key_names authorizes existing keypairs on the instance. The public keys are
baked into the seed at launch, so deleting the keypair afterwards does not
remove access from an instance already running.
user_data is base64-encoded cloud-init, and the platform merges it over a
base configuration that creates the login user:
A
user_data document that is not valid YAML fails the build, and the
instance lands in error rather than booting without it.
Giving the instance an identity
iam_role_id attaches an IAM role. Software inside the guest then fetches
short-lived credentials from the metadata service at 169.254.169.254 — no
access key is written to the instance, and nothing has to be rotated.
Two conditions, and both are easy to miss. The role’s trust policy must
accept
crn:compute:*:*:instance/* (or the specific instance CRN), and the
principal making the launch call needs iam:PassRole on the role,
separately from compute:CreateInstance. Without the trust policy the
instance launches and the credentials never mint; without iam:PassRole the
launch itself is denied. Roles and instance identity walks
through both.The instance lifecycle
An instance carries two states and they answer different questions.vm_state
What you asked for. It moves the moment the API accepts your call.
power_state
What the guest is actually doing, as last observed on the host.
POST /v1/instances/{instance_id}/start leaves vm_state
reading running immediately, while the guest is still booting. To know
whether an instance is really up, read power_state.
You may also see
migrating on an instance the platform is moving between
hosts. It is transient, nothing you asked for, and it returns to running.
Treat an unrecognized state as “busy, do not act” rather than as a failure.Start, stop, reboot
Each is a202 with an empty body — poll the instance to see the result.
Start
Start
POST /v1/instances/{instance_id}/start accepts an instance in stopped
or in error — the second case is how you retry an instance that
failed on the way up, without recreating it. Any other state is a 409.Stop
Stop
POST /v1/instances/{instance_id}/stop requires running. Anything else
is a 409 — including an instance already stopped, so this is not a
“make it stopped” idempotent call.Reboot
Reboot
POST /v1/instances/{instance_id}/reboot requires running. The default
is a graceful ACPI reboot the guest can act on; {"hard": true} is a power
cycle — the reset button, with no chance to flush anything.Resize
POST /v1/instances/{instance_id}/resize with a flavor_id changes vCPU
and RAM, and nothing else. Disks are untouched, addresses are untouched, the
guest’s data is untouched.
Two things about it are worth knowing before you plan a resize window:
1
The instance must be stopped
A running guest’s CPU and memory maximums cannot be changed underneath it,
so a resize on a running instance is a
409. Stop it, resize, start it.2
The new size is applied at the next start
The call records the target and returns. The guest comes up on the new
flavor when you start it — there is no separate confirm step, and no
state in which the instance is half-resized.
400: resizing to the flavor the instance already
uses, and resizing onto a loadbalancer or database flavor.
An instance’s network ceiling moves with the flavor as part of the resize, so
a downsize gives up the larger flavor’s bandwidth too.
Reinstall
POST /v1/instances/{instance_id}/reinstall re-images the boot disk while
keeping the instance itself. Also stopped-only, and also applied at the next
start.
Kept
The instance id, name, CRN, IP addresses and MAC addresses, its network
interfaces, its keypairs, its cloud-init seed, and every attached data
volume.
Replaced
The boot volume. A fresh one is cloned from the image and swapped in, and
the old one is deleted. Everything on the root filesystem is gone.
image_id and it reinstalls from the image the
instance already has; omit size_gb and the replacement is the image’s
min_disk_gb; omit volume_type and it lands on the region default. The same
floor applies as at launch — size_gb must be at least the image’s
min_disk_gb, and within 1–16384 GB.
Because the addresses survive, reinstall is the operation for “same machine,
clean OS” — a rebuild where anything pointing at the instance keeps working.
Delete
DELETE /v1/instances/{instance_id} answers 202. The instance moves to
deleting, the guest is torn down, and the row is removed once its interfaces
and volumes have been reclaimed — so a completed delete stops answering reads
entirely rather than leaving a deleted row behind.
Deleting an instance already deleting is accepted and re-drives the teardown
rather than starting a second one.
What survives a delete
Volumes
GET /v1/instances/{instance_id}/volumes lists the attachments in boot order,
boot disk first, each resolved with the volume’s current name, tier, size and
status.
available, and the
instance must be running or stopped. The disk is hot-plugged; device
picks the next free slot (vdb, vdc, …) unless you name one.
With mount_path set, the in-guest agent formats the disk — only if it is
blank — and mounts it there. Leave it empty and you get the block device and
nothing else.
Detaching is DELETE /v1/instances/{instance_id}/volumes/{volume_id}, also
202, also requiring running or stopped.
Network interfaces
That listing is ordered by boot index, primary first, and each entry resolves the interface’s current MAC, IPv4, IPv6, subnet and VPC.Attaching and detaching
POST /v1/instances/{instance_id}/nics does one of two different things, and
you pick by which field you send — exactly one of them:
- subnet_id — a fresh NIC
- interface_id — an existing interface
Provisions a new interface in that subnet, allocating an address and
attaching any
security_group_ids you name. ip_address and mac are
optional overrides; a MAC must be locally administered. Detaching this
interface later destroys it along with its address.running or stopped; the device is hot-plugged, or
appears at the next start for a stopped instance.
Public addresses
networks[].assign_public_ip allocates a floating IP and attaches it to that
interface. Each one is a real allocation: it counts against your account’s
floating_ips quota and is billed like any other.
The interface’s subnet must already route 0.0.0.0/0 to an internet gateway.
Without that route the address would be allocated and silently unreachable, so
the launch fails instead.
The older top-level
assign_public_ip still works and means the primary NIC
— the only interface it could ever have addressed. It is folded into
networks[0], so sending it and networks[0].assign_public_ip asks for
the same address once, not for two.Console access
Three endpoints, for three different situations, and each requires its own IAM action — reading a boot log, taking a picture of the screen, and holding a keyboard inside the guest are deliberately not the same grant.Console output — the boot transcript
Console output — the boot transcript
GET /v1/instances/{instance_id}/console/output returns what the guest
wrote to its serial console during its current boot: a bad fstab, a
wrong kernel, a cloud-init failure. Nothing has to be installed in the
guest, and it works on a stopped instance — which is exactly when you need
it.max_bytes is a ceiling you may lower, not raise: the maximum is 65536 and
larger values are clamped to it. When the transcript is longer than what
you asked for, its beginning is dropped and truncated is true. The
end is always kept.The transcript resets at every start, so a crashed guest’s last transcript
is gone once it restarts — read it before you reboot. An instance that has
never booted returns empty output, not an error.Requires compute:GetConsoleOutput.Screenshot — what the screen shows
Screenshot — what the screen shows
GET /v1/instances/{instance_id}/console/screenshot returns a still image,
normally image/png.This is the counterpart for everything a serial transcript cannot reach: a
guest sitting in its boot manager, at a GRUB prompt, panicking before
serial init, or booted from an image whose kernel was never told to log to
the serial port. In those the transcript is empty and the screen holds the
whole answer.A still, not a session — there is no remote desktop. The instance must be
running; a stopped one has no display to capture and answers 409 rather
than a blank frame.Requires compute:GetConsoleScreenshot.Serial console — an interactive session
Serial console — an interactive session
GET /v1/instances/{instance_id}/console/serial upgrades to a WebSocket
carrying an interactive session on the instance’s serial port. This is the
way in when the network is broken: a wrong kernel, a full disk, a security
group that locked you out.Raw bytes in binary frames, both directions. It is a terminal, not a
protocol — point a terminal emulator at it. Authentication rides on the
upgrade request, signed like any other call, so there is no separate token
step. A non-WebSocket request answers 426.backlog_bytes replays already-written output before live output begins,
so attaching to a quiet guest shows why it is quiet instead of an empty
screen. Default 32768, maximum 65536, 0 disables it. Replay and live
output are one stream read forward, so nothing is lost or duplicated at
the join.One session per instance: opening a second disconnects the first,
rather than interleaving two people’s keystrokes. A session ends after 15
minutes idle, or 4 hours regardless, and the close frame says which.Requires compute:StartSerialConsole, and the instance to be running.This drops you at the guest’s own login prompt. It is not a backdoor —
the guest’s credentials are still required, and nothing here grants
access past what the guest itself allows.
Names, tags and metadata
unique per account
1–128 characters, DNS-safe: letters, digits, dot, dash, underscore, starting
and ending alphanumeric. A name that collides with another instance in the
account is a
409.not supported
PATCH /v1/instances/{instance_id} edits description, metadata and
tags. A name is fixed for the life of the instance.IAM and cost
Read by IAM policy conditions as
basalt:RequestTag/<key> on the create and
basalt:ResourceTag/<key> afterwards, and used for cost attribution. See
policies.replaced, not merged
The map you
PATCH becomes the whole set of your keys.Metadata keys under the
basalt: namespace are control-plane state — how an
instance proves which pool or managed resource it belongs to. You cannot set
them, and you cannot remove them: they are preserved through a PATCH
whether or not you echo them back, and proposing a different value for one
is a 400 rather than a silent drop. Read-modify-write against the whole
metadata map is therefore safe.Instances you did not create
Managed products run on instances too. A load balancer’s replicas and a database cluster’s nodes appear inGET /v1/instances with managed_by set.
They are read-only here: reads and console access work, and every lifecycle
and mutation call answers 404 — stop, resize, delete, NIC and volume
changes alike. Act on them through the resource that owns them.
Quotas
An instance holds three regional counters at once, all reserved together at create and released at delete:instances, vcpus and ram_mb. That is why
a small instances limit is not the whole story — a handful of large flavors
can exhaust vcpus first.
volumes_per_instance caps the boot disk plus every data volume on one
instance, and is checked before the instance row exists, so exceeding it is a
plain 4xx rather than an instance that provisions partway and parks in
error. Floating IPs come out of the network service’s floating_ips.
CRNs
Keypair CRNs are built from the name, so an IAM policy can wildcard a
naming convention:
crn:compute:sa-saopaulo-1:my-account:keypair/ci-*.
Troubleshooting
vm_state is error right after create
vm_state is error right after create
Read
fault. It carries a code, a message and free-form details from
the step that failed, plus the time it happened.POST /v1/instances/{instance_id}/start accepts an instance in error, so
a failure that happened while the guest was coming up can be retried
without recreating anything.It does not rebuild what was never built. A failure earlier than that — the
boot volume or the interfaces — leaves nothing for a start to bring up, and
the instance is best deleted and created again once the cause is fixed. The
usual causes are in the request: a subnet that does not route to an
internet gateway while a NIC asked for a public address, a user_data
document that is not valid YAML, or a boot size below the image’s floor.The instance says running but nothing answers
The instance says running but nothing answers
vm_state is what you asked for; power_state is what was observed. A
running instance whose power_state is shutdown, crashed or
nostate has not come up.Read the console output next — it works on a guest that
never reached the network, which is precisely the case SSH cannot
diagnose.My cloud-init never ran
My cloud-init never ran
Two causes, both silent.Your
user_data has to decode to a document starting with
#cloud-config. A shell script is not merged and is not executed — put
the commands in runcmd.Or the merge replaced something. Top-level keys replace wholesale, so a
users: block of your own removes the default basaltic user and the
authorized keys inside it, which reads from outside as “the instance
booted and I cannot log in”.I cannot SSH in
I cannot SSH in
The default login user is
basaltic, not root and not the image’s
own default — password login and root login are both disabled in the base
configuration.Check that key_names named a keypair that existed at launch, and that the
NIC’s security group admits your source on port 22. Confirm the address
from GET /v1/instances/{instance_id}/nics rather than from public_ip,
which only reports the primary interface.Resize is refused with a capacity error
Resize is refused with a capacity error
An instance does not move hosts when it resizes, so the growth has to fit
where it already is. A
dedicated flavor needs whole free threads, not
merely an undersubscribed host.The way around it is to launch a new instance on the target flavor — the
scheduler is free to place that one anywhere — and move the workload, or
to reinstall onto a fresh instance and reattach the data volumes.A 409 on start, stop or reboot
A 409 on start, stop or reboot
Each action accepts a narrow set of states: start needs
stopped or
error, stop and reboot need running, resize and reinstall need
stopped. Anything mid-transition — pending, building, stopping,
rebooting, deleting — is refused until it settles. Poll vm_state and
retry.An operation on an instance answers 404 but I can see it
An operation on an instance answers 404 but I can see it
Check
managed_by. An instance owned by a load balancer or a database
cluster is read-only through this API, and every mutation answers 404
rather than 403 — the customer surface does not admit it is actionable.A 403 is the other case, and a different one: the instance is yours and
the IAM check on that specific action failed. Actions are per-operation, so
a principal may read an instance and still not be allowed to stop it.Next
Images and keypairs
Importing an image, how a name moves between builds, and what the listing
hides.
Instance pools
A managed set of identical instances, with rolling replacement and a
shared public address.
Roles and instance identity
Trust policies,
iam:PassRole, and credentials from the metadata service.API reference
Every compute operation, with request and response schemas.