Names are movable tags
How
debian-13 and debian-13:20260807 differ, and when the tag moves
under you.Importing
Presigned URL in, background conversion, and what the status column tells
you.
What the listing shows
Why a build you published yesterday is missing, and the flag that brings
it back.
Keypairs
Generate or import, the one-time private key, and what deleting one does
not do.
Images
Public and private
GET /v1/images returns your own images plus the public platform catalog —
the maintained OS images every account can boot. Platform images carry no
account in their CRN (crn:compute:<region>::image/<id>), so they read the same
for everyone.
A name is a tag, a version is a build
Every image row has aname and a version, and they do different jobs.
name
A movable tag, shared by every build behind it.
debian-13 points at
whichever build is current for its (name, architecture).version
Identifies one build within that name, and must be unique there. Omit
it on import and the server stamps a UTC timestamp, so every build is
addressable whether or not you labelled one.
is_current on the image says whether it is the name’s current target. Older
versions stay fully bootable by id and by name:version — promotion moves a
pointer, it does not retire anything.
Publishing a version a tag already carries is a
409, not a second anonymous
build. The error says which half to change: rename and you publish a second
tag for the same release, which is the mess the constraint exists to prevent
— change the version instead.Importing an image
No image bytes flow through the API. You upload the disk to a bucket you control — the Basaltic object store, S3, MinIO, anything — with a real multipart S3 client, then hand over a presigned GET URL.1
Register the import
202 with status: "importing". Accepted formats are
qcow2, raw, vmdk, vhd, vhdx and vdi; all are converted to a raw
base on import, and format describes what is at source_url.2
Wait for the fetch and conversion
A worker fetches the URL once, converts the disk and imports it. The URL is
not retained afterwards, so it only has to stay valid long enough to be
read.
3
Poll until active
active means bootable, and size_bytes is filled in from what was
actually written. error means the import gave up; import_error says
why.import_error appearing does not mean the import has stopped. Each failed
attempt writes the reason onto the row so a customer polling it can see
what is going wrong, and the import is retried. The row reaching error is
what says nothing further will be attempted.Publishing without switching the tag
current defaults to true: a completed import becomes the name’s current
version and future launches of that bare name boot the new bits. Send
"current": false to stage a build without switching, then promote it later:
(name, architecture)
is demoted in the same operation. The same call is how you roll back: point
the name at the older build and launches follow it immediately.
The switch happens when the import completes, not when it is accepted. A
new build is
importing for as long as the conversion takes, and the tag
keeps pointing at the previous build for all of it — so publishing over a
name in use never leaves it resolving to something that cannot boot.Only an active image can be made current; asking to promote one that is
still importing is a 400.What List images returns
GET /v1/images is a listing of what is worth launching, not of every row
you own. One tag normally contributes one entry.
A build drops out only when a newer build holds its name. That is
superseded history, and nothing else — the predicate is deliberately narrow:
Still listed: anything importing or errored
Still listed: anything importing or errored
Whatever its age. These are rows you have to deal with — an import you are
waiting on, or one that failed and is still holding an image slot until you
delete it.
Still listed: a newer version staged with current: false
Still listed: a newer version staged with current: false
“Not current” is the wrong test on its own. A version you staged
deliberately, and a version you rolled back from, are both not current
and both still yours to act on. Only being superseded by something newer
takes a build out of the listing.
all_versions=true for a tag’s whole history. Results are ordered by
name, and paginate through meta.marker like every other listing.
The other filters are os, architecture, visibility, status and name
— which is a substring match, not a prefix or an exact one.
End of life
eol_date records the day an OS release stops receiving free security updates
for a default install. Absent means nobody has recorded one, which is not
the same as supported indefinitely.
Omit eol_date on a new build and it inherits the date the name’s current
version carries, so republishing a tag cannot quietly stop tracking its
release. An explicit null on PATCH clears it; omitting the field leaves it
alone.
An eol_date on your own image is stored and shown, and nothing acts on
it. The platform never hides an image you own — there is no un-hide, so
withdrawing your image would be a silent, unrecoverable delete of something you
still hold.
Deleting an image
DELETE /v1/images/{image_id} answers 202 and marks the image hidden, so
clones already in flight can finish. The underlying data is reclaimed as soon
as nothing is cloned from it — immediately when no instance was built from the
image, and by a background sweep otherwise.
Superseded builds of your own images are never reclaimed for you. Every
build you publish holds an
images slot and its bytes against
image_storage_gb until you delete it — so a pipeline that publishes on
every commit needs a deletion step, or the quota becomes the deletion step.404. A platform image the catalog
withdrew at end of life keeps its data on purpose, so deleting it by id is how
you ask for that data to go.
Sizes
enforced floor
The smallest boot volume that can hold the image. Defaults at import to the
image’s virtual size rounded up. A launch or reinstall asking for less is
refused.
recorded, not enforced
What the image is documented to need. It is stored and returned for you to
read; nothing stops an instance booting on a flavor below it.
Keypairs
A keypair is an SSH public key stored under a name in your account. A name is 1–128 characters of letters, digits, dot, dash and underscore, starting and ending alphanumeric, and unique per account. It goes straight into the CRN —crn:compute:<region>:<account>:keypair/<name> — so an IAM policy can fence a
naming convention rather than a list of ids.
authorized_keys line shape — ssh-ed25519,
ssh-rsa, ecdsa-sha2-nistp256, with or without a trailing comment.
Generated keys are ed25519. fingerprint is the SHA256 form (SHA256:…),
which is what ssh-keygen -lf prints by default.
Using a keypair
Name keypairs at launch withkey_names, and their public keys are written
into the instance’s cloud-init seed. Several are fine — every named key ends up
in the default user’s authorized_keys.
The keypairs an instance was launched with are embedded in the instance itself,
so GET /v1/instances/{instance_id} shows them without a second call.
Troubleshooting
An image I just published is not in the listing
An image I just published is not in the listing
Check whether a newer build holds the same name. The default listing shows
one entry per tag, and drops builds a newer one has superseded. Add
?all_versions=true to see the history — the build is still there and
still bootable by name:version or by id.Launches picked up a different image than last week
Launches picked up a different image than last week
A bare
name follows the tag, and the tag moves when a new build is
published as current. Pin name:version or an id in anything that has to
be reproducible; keep the bare name for “always the latest”.A name stopped resolving
A name stopped resolving
Two causes. A platform release past end of life has been withdrawn — the
error names the release and the date. Or the name has no current version,
which happens after a
PATCH with current: false on the only current
build: the tag then points at nothing, though every build behind it still
launches by name:version. Promote one to fix it.An import sits in importing forever
An import sits in importing forever
Read
import_error — a failed attempt records the reason on the row while
retrying. The usual causes are a presigned URL that expired before the
fetch, a format that does not match the bytes at the URL, or a host that
resolves to a private address and is refused.Note that an import which finishes but exceeds your image_storage_gb
quota is refused and not retried: rebuilding the same bytes would cost
a full download and conversion to reach the same answer.POST /v1/images answers 409
POST /v1/images answers 409
That name already carries that version. A name is shared by every build
behind it on purpose, so publish under a new
version rather than a new
name.I cannot see another account's image
I cannot see another account's image
That is the design. Only the platform catalog crosses accounts;
visibility: "public" on your own image does not share it, and an image id
belonging to another account answers 404 rather than 403, so the API
never confirms it exists.Next
Launching instances
Flavors, boot volumes, cloud-init and the instance lifecycle.
Instance pools
Where an image reference is resolved once, at create, and every replica
boots the same build.