Replace the cloud-init user-data on a STOPPED instance.
curl --request PUT \ --url https://stratum.example.com/v1/projects/example/compute/instances/example/user-data \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: text/plain' \ --data binaryAWS-shape counterpart to ec2 modify-instance-attribute --user-data. The instance MUST be in stopped state — a 409
with INVALID_STATE fires otherwise. The next start rebuilds
the cidata ISO from the new bytes; IMDS /latest/user-data
serves them on the same boot.
Body: raw bytes (Content-Type is ignored; pass whatever cloud-
init expects via --data-binary). Cap is 64 KiB.
On success, the previous user-data blob’s refcount is decremented; identical content is deduped via the existing sha256-keyed blob store, so re-PUT’ing the same bytes is a no-op at the storage layer.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Request Bodyrequired
Section titled “Request Bodyrequired”Responses
Section titled “Responses”Updated instance.
object
Catalog name (e.g. m1.large); references the instance-type
catalog. Empty for custom-shape launches — read
vcpus + memoryBytes for the effective shape in that case.
Effective vCPU count. Populated for every instance mirrors the catalog entry for catalog launches; carries the operator’s override for custom-shape launches.
Effective memory in bytes. Populated for every instance (catalog or custom).
Empty until the scheduler stamps it.
customer-visible state.
Human-readable detail set when state=error.
aggregated, read-time instance-health roll-up, derived from durable signals (lifecycle state + agent heartbeat recency). healthy: running, agent heartbeating. degraded: running and heartbeating but the host can’t read a fresh domain sample (or vice-versa). unhealthy: running with no agent heartbeat, or state=error. migrating: a cold migration is in flight. unknown: transitional or deliberately-down states where the lifecycle state itself is the signal, and an agent enabled after launch whose install command has not been run yet. agent_disabled: agentEnabled: false, at launch or since, so the agent’s absence is the configured intent, not a fault — distinct from unhealthy precisely so an appliance image or an operator who declined the agent does not read as broken forever. Stable across a control-plane restart (the in-memory session channel is not an input).
Why health reads the way it does. Present only on the
describe path (GET-by-id), and only when health needs explaining;
omitted for a healthy instance.
Read this before acting on health: unhealthy: it is what separates an
agent that never arrived (permanent — rebuild) from one that has merely
gone quiet (often self-resolving), and it carries the first-heartbeat
deadline so a normal launch is not mistaken for a broken one.
object
Machine-readable cause.
agent_pending: running, no heartbeat yet, still inside the
first-heartbeat grace window. health already reads unhealthy (it flips
within ~3 minutes), so this is what distinguishes a perfectly normal
launch from a broken one — see deadline.
agent_never_reported: past the grace window having never reported.
Permanent without intervention; run enable-agent for a new install
command, or disable-agent if the instance is not meant to have an
agent.
agent_gone_quiet: reported at least once and then stopped. The agent was
installed correctly, so this is not a delivery bug; frequently
self-resolving.
agent_disabled: agentEnabled: false, declined at launch or
disabled since. Not a fault — reported rather than omitted so
a reader who expected an agent learns why there is none instead of
hunting a failure that never happened.
agent_enrollment_pending: enable-agent ran and its install command has
not been run in the guest yet. health reads unknown;
nothing has failed.
Human-readable explanation, including what to do about it. Not a stable contract — switch on code.
When the reported condition began — launch (or enrollment, for an agent added later) for an agent that never reported, the last heartbeat for one that has gone quiet.
When a pending agent stops being given the benefit of the doubt. Set only for agent_pending; after this the code becomes agent_never_reported.
Whether the instance is meant to run the in-VM Stratum agent.
Set at create (CreateInstanceRequest.agentEnabled); changed later only
by the enable-agent and disable-agent actions.
False means the agent is not wanted, so health reports
agent_disabled rather than unhealthy and the instance is excluded
from agent-presence reconciliation. Read this before concluding an
instance is broken because it never reported.
enable-agent ran and its install command has not been run
in the guest yet. health reads unknown (reason
agent_enrollment_pending) until it is.
When an enable-agent install command last enrolled the
agent, or, with agentActivation: automatic, when
enable-agent turned it on. Absent for an agent installed at launch.
The first-heartbeat grace window runs from here rather than from
launchedAt.
How the agent is turned on in the guest after launch, fixed
at launch from the image the instance launched from. It changes once
at most: enable-agent with force: true turns
automatic into install-command for good, for a guest whose dormant
package was removed.
automatic: the image ships the agent installed but dormant, with a
timer that follows the agent flag. enable-agent and disable-agent
take effect in the guest within a minute, with nothing to run there,
and disabling keeps the package installed.
install-command: any other image. enable-agent returns a one-time
install command to run in the guest, and disable-agent removes the
agent.
Images created from the instance (create-image, image builder output)
carry the stratum.dev/agent-activation: automatic tag only while it
reads automatic.
In-VM agent liveness, embedded on the instance
describe (GET-by-id) response so a detail view has agent
health without a second round-trip to .../agent-status. Present only on the describe path; omitted on
list and the action responses. An instance whose agent has
never reported carries health: unknown.
object
Derived agent liveness. agent_disabled: the instance’s agentEnabled is false, so no agent is wanted and its silence is not a fault. enrollment_pending: enable-agent ran and its install command has not been run in the guest yet. Exactly the instance fields, so the two never disagree.
When the control plane last received a heartbeat.
The agent’s reported build version.
Result of the agent’s most recent reconcile (“ok” or “error”).
When the agent last completed a reconcile.
True when the root volume was auto-provisioned by CreateInstance and is cleaned up on terminate.
object
Guest device path (e.g. /dev/vdb).
Set when the launch cloned this volume from a multi-volume image’s data device.
The image snapshot this volume was cloned from at launch.
Primary network interface ID. Set by the launch path when SubnetIDs are supplied or the project’s default subnet is resolved.
True when the primary NIC was auto-provisioned by CreateInstance and is cleaned up on terminate.
Floating IP attached to the primary NIC, when one was allocated at launch via floatingIPPool / –public-ip.
True when the primary FIP was auto-allocated by CreateInstance and is released on terminate.
object
The NIC’s primary private IPv4 address within its subnet. Empty until the NIC is scheduled onto a subnet and an address is assigned (e.g. while the instance is still creating).
The subnet this NIC actually landed in, enriched at read time from the network NIC row.
Nothing on the instance reported placement before. A create request naming subnetId instead of the real subnetIds had the field silently dropped, returned 201, and attached the NIC to the default platform network — with no response field distinguishing that from a correctly placed instance.
The VPC the NIC’s subnet belongs to.
AWS-parity alias of primaryPrivateIP.
The instance’s public DNS name,
<instance-id>.compute.<parent> (the AWS PublicDnsName). The install’s
authoritative DNS answers it with primaryPublicIP, and with the
private IP when asked from inside the instance’s own VPC.
Set while that record is published: the instance has a floating IP on
its primary NIC, its VPC has dnsHostnames on, and the install has a
DNS parent domain. Omitted otherwise. The record follows within one
reconciler pass (30 s) of the floating IP being associated or the VPC
setting changing. The same name IMDS serves at
meta-data/public-hostname.
For a name of your own, create an alias record in a hosted zone. An
aliasTarget of type: fip answers the floating IP’s address; one of
type: instance answers the instance’s private IP and publishes a
public.<name> companion for its floating IP. Both follow the address
as it changes.
The placement group this instance was placed by. Absent when it joined none.
It can become absent without the instance changing: deleting a group leaves its members running and drops the reference. That is losing the guarantee, not moving between groups.
Name of the placement group this instance was placed by — what
the tenant declared at launch. Absent when it joined none, and
absent alongside placementGroupId once the group is deleted.
SHA-256 over the user-data bytes, sha256:<hex>.
Reserved for instance profiles (/ 2.x).
Attached instance profile. Apps inside the VM that hit IMDS get short-lived credentials for the profile’s service account. Empty when no profile is attached.
object
When true the controlplane queues a start
lifecycle action against this instance on boot if libvirt
currently has the domain shut off. Default false.
The power state the operator has ASKED for, as distinct from
state, which is what the instance actually is. A leader-gated
reconciler converges the two, so setting this
returns immediately rather than blocking for a guest shutdown.
They are separate fields deliberately: collapsing them would make “someone
stopped this by hand” indistinguishable from “this is meant to be
stopped”. An instance manually stopped while desiredState=running is
started again on the next pass.
Only running and stopped are accepted. starting/stopping are
transitions the worker owns and are never a destination.
True while a stop asked with force: true (a hard stop, the Multi-AZ fence) is in flight; cleared when the instance leaves stopping.
Per-instance AWS-shape IMDS options. Returned on
the Instance and settable at launch (CreateInstanceRequest) or via the
modify-metadata-options action. httpEndpoint is enforced: when
disabled the whole IMDS surface (including the Stratum SSH-CA agent
plane) is off for the instance. httpTokens + httpPutResponseHopLimit
are recorded for AWS parity only — Stratum’s IMDS always requires a
session token (effectively IMDSv2-only), so optional does not relax
that in v1. Omitted fields default to enabled / required / 1.
object
Whether IMDS is reachable for the instance. Default enabled.
IMDSv2 token requirement (recorded; Stratum is always v2). Default required.
Recorded for AWS parity. Default 1.
When enabled the guest can read its own tags at /latest/meta-data/tags/instance/. Default disabled; the image builder enables it on a build instance.
Example
{ "state": "creating", "health": "healthy", "healthReason": { "code": "agent_pending", "message": "the agent has NEVER checked in, 4 days after it was installed — it was not installed, or it cannot reach IMDS. This will NOT resolve on its own and no retry is scheduled: run enable-agent for a new install command and run it in the guest, or disable the agent if the instance is not meant to have one." }, "agentActivation": "automatic", "agentStatus": { "instanceId": "i-A8c2D4e6F", "health": "healthy" }, "networkInterfaces": [ { "privateIp": "172.31.0.42", "subnetId": "subnet-6arpymeu32", "vpcId": "vpc-puhtjesijq" } ], "desiredState": "running", "metadataOptions": { "httpEndpoint": "enabled", "httpTokens": "optional", "instanceMetadataTags": "enabled" }}Bad request.
object
The findings of a rejected IAM policy document (custom and resource policies).
One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.
object
The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.
Index of the statement at fault; -1 for the document as a whole.
The offending action, where one is at fault (ACTION_NOT_APPLICABLE).
Field errors, for codes that carry them: each problem at its field
(see FieldError). Documented on the responses that set it, e.g.
approveNodeJoin’s INVALID_INPUT and JOIN_APPROVAL_REFUSED.
Absent otherwise.
One field’s problem, the stage-2 installer API’s shape.
field is the field’s path, message what is wrong with it.
object
The field’s path, e.g. links[0].interface, disks[1].confirm,
mgmt.address or role; empty for the whole request.
Structured facts about the refusal, for codes that carry them. The
shape is fixed per code and documented on the response that answers
it, e.g. IMAGE_POOL_MISMATCH (see ImagePoolMismatchDetails) and
IMAGE_IN_USE. Absent for codes that carry none.
object
Example
{ "issues": [ { "code": "ACTION_NOT_APPLICABLE", "action": "compute:CreateVpc" } ]}Resource not found.
object
The findings of a rejected IAM policy document (custom and resource policies).
One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.
object
The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.
Index of the statement at fault; -1 for the document as a whole.
The offending action, where one is at fault (ACTION_NOT_APPLICABLE).
Field errors, for codes that carry them: each problem at its field
(see FieldError). Documented on the responses that set it, e.g.
approveNodeJoin’s INVALID_INPUT and JOIN_APPROVAL_REFUSED.
Absent otherwise.
One field’s problem, the stage-2 installer API’s shape.
field is the field’s path, message what is wrong with it.
object
The field’s path, e.g. links[0].interface, disks[1].confirm,
mgmt.address or role; empty for the whole request.
Structured facts about the refusal, for codes that carry them. The
shape is fixed per code and documented on the response that answers
it, e.g. IMAGE_POOL_MISMATCH (see ImagePoolMismatchDetails) and
IMAGE_IN_USE. Absent for codes that carry none.
object
Example
{ "issues": [ { "code": "ACTION_NOT_APPLICABLE", "action": "compute:CreateVpc" } ]}Conflicting state.
object
The findings of a rejected IAM policy document (custom and resource policies).
One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.
object
The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.
Index of the statement at fault; -1 for the document as a whole.
The offending action, where one is at fault (ACTION_NOT_APPLICABLE).
Field errors, for codes that carry them: each problem at its field
(see FieldError). Documented on the responses that set it, e.g.
approveNodeJoin’s INVALID_INPUT and JOIN_APPROVAL_REFUSED.
Absent otherwise.
One field’s problem, the stage-2 installer API’s shape.
field is the field’s path, message what is wrong with it.
object
The field’s path, e.g. links[0].interface, disks[1].confirm,
mgmt.address or role; empty for the whole request.
Structured facts about the refusal, for codes that carry them. The
shape is fixed per code and documented on the response that answers
it, e.g. IMAGE_POOL_MISMATCH (see ImagePoolMismatchDetails) and
IMAGE_IN_USE. Absent for codes that carry none.
object
Example
{ "issues": [ { "code": "ACTION_NOT_APPLICABLE", "action": "compute:CreateVpc" } ]}