Skip to content

Create an image recipe.

POST
/v1/projects/{projectId}/compute/image-recipes
Code sample: Shell / cURL
curl --request POST \
--url https://stratum.example.com/v1/projects/example/compute/image-recipes \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "name": "example", "displayName": "example", "description": "example", "base": { "imageId": "example", "catalog": "example", "imageName": "example", "recipeId": "example", "latest": true }, "buildInstance": { "instanceType": "example", "subnetId": "example", "securityGroupIds": [ "example" ], "iamInstanceProfileId": "example", "keyPairName": "example" }, "steps": [ { "name": "example", "cloudInit": "example", "runCommand": "example", "sudo": true, "timeoutSeconds": 1 } ], "tests": [ { "name": "example", "runCommand": "example", "sudo": true, "timeoutSeconds": 1 } ], "output": { "namePattern": "example", "description": "example", "visibility": "private", "sharedWith": [ "example" ], "tags": { "additionalProperty": "example" }, "keepLast": 1, "keepLastAction": "deprecate" }, "tags": { "additionalProperty": "example" }, "distribution": { "launchTemplates": [ { "launchTemplateId": "example", "setDefault": true } ], "instanceRefresh": { "autoScalingGroupIds": [ "example" ], "minHealthyPercentage": 1, "maxBatchSize": 1, "instanceWarmupSeconds": 1 } }, "schedule": { "cron": "0 3 * * 1", "timeZone": "example", "onlyIfBaseChanged": true } }'

Declares what to build: a base, the throwaway build instance’s shape, cloud-init and run-command steps, tests, and the output image. Nothing runs until actions/build. Gated on compute:CreateImageRecipe; quota compute.image_recipes.count.

projectId
required
string
/^p-[a-z0-9-]+$/
Media typeapplication/json

Create (POST) or replace (PUT; name is fixed) an image recipe.

object
name
required
string
/^[a-z][a-z0-9-]{1,38}[a-z0-9]$/
displayName
string
description
string
base
required

Where a build starts from. Exactly one of imageId, catalog, imageName, recipeId is set. imageName is a glob over the project’s own active images and recipeId chains to another recipe’s last succeeded output; both resolve to the newest match at build time.

object
imageId
string
/^img-[a-z0-9-]+$/
catalog

A /v1/images/catalog key (e.g. debian-13) — the active public image imported from it.

string
imageName

A glob over the project’s own image names (e.g. web-golden-*).

string
recipeId

Another recipe of the project; its last succeeded build’s output.

string
/^irc-[a-z0-9-]+$/
latest

Always true for imageName and recipeId at 2.0.

boolean
buildInstance
required

The throwaway build instance’s shape; it is launched in the recipe’s project and always terminated.

object
instanceType
required
string
subnetId

Omitted = the project’s default subnet.

string
/^subnet-[a-z0-9-]+$/
securityGroupIds
Array<string>
iamInstanceProfileId
string
/^iprof-[a-z0-9]+$/
keyPairName
string
steps
Array<object>
<= 32 items

One provisioning step: exactly one of cloudInit (a cloud-config or shell document; every cloud-init step is concatenated into the build instance’s user-data and runs as root at first boot) or runCommand (run in order through run-command once the instance is up, as the agent’s login user). sudo: true runs a run-command step as root granted by the instance agent only on an instance carrying the stratum.dev/image-build tag it reads from IMDS, never on an ordinary instance, and never written to the guest.

object
name
required
string
cloudInit
string
runCommand
string
sudo
boolean
timeoutSeconds

Per-step deadline for a run-command step (default 600).

integer
>= 1 <= 600
tests
Array<object>
<= 32 items

A run-command whose non-zero exit fails the build (no image is produced).

object
name
required
string
runCommand
required
string
sudo
boolean
timeoutSeconds
integer
>= 1 <= 600
output

The image a build produces.

object
namePattern

A Go template over {{.Recipe}}, {{.BuildNumber}}, {{.Date}} (YYYYMMDD); default {{.Recipe}}-{{.BuildNumber}}. Must render a valid image name.

string
description
string
visibility

Default private; public publishes the output (subject to the org’s public-images toggle); shared shares it with sharedWith — and publishes instead while none of those projects exist yet (the operator recipes name projects that arrive with /069).

string
Allowed values: private shared public
sharedWith

Project ids, with visibility: shared.

Array<string>
tags

AWS-shape resource tags. Freeform UTF-8 keys (1-128 bytes, no leading/trailing whitespace) mapped to freeform UTF-8 values (0-256 bytes). Max 50 tags per resource. Reserved prefixes (stratum.dev/, stratum.platform_health., stratum:, aws:) return 400 RESERVED_NAMESPACE on customer input. PATCH replaces the whole map..

object
<= 50 properties
key
additional properties
string
<= 256 characters
keepLast

After a successful build, retire the recipe’s older outputs beyond N (0 = keep all). What “retire” means is keepLastAction.

integer
keepLastAction

How keepLast retires an older output. deprecate (the default) hides it from default listings and launches keep working; disable refuses launches; delete deregisters it.

Only delete frees compute.images.count — the quota counts every image the project still owns, whatever its lifecycle state. A recipe that builds on a schedule and only deprecates will therefore fill its project’s image quota, and once it is full no build in that project can finish. The default stays deprecate so that upgrading never starts deleting images by itself; the seeded operator recipes ask for delete.

A delete is never forced: an image a launch template still names is left alone and the refusal is logged, so retiring cannot break a live Auto Scaling group.

string
default: deprecate
Allowed values: deprecate disable delete
tags

AWS-shape resource tags. Freeform UTF-8 keys (1-128 bytes, no leading/trailing whitespace) mapped to freeform UTF-8 values (0-256 bytes). Max 50 tags per resource. Reserved prefixes (stratum.dev/, stratum.platform_health., stratum:, aws:) return 400 RESERVED_NAMESPACE on customer input. PATCH replaces the whole map..

object
<= 50 properties
key
additional properties
string
<= 256 characters
distribution

The “bake → roll” half. After a successful build the builder creates a new version of each launch template with the output image (every other field copied from $Latest, setDefault as configured), then starts a instance refresh per group toward $Latest. Saving a recipe with launchTemplates needs asg:CreateLaunchTemplateVersion on each template and with instanceRefresh asg:StartInstanceRefresh on each group. A refresh refused (one already in progress) is recorded on the build as that target’s error; the build still succeeds and the version stays.

object
launchTemplates
Array<object>
object
launchTemplateId
required
string
setDefault
boolean
instanceRefresh
object
autoScalingGroupIds
required
Array<string>
minHealthyPercentage
integer
<= 100
maxBatchSize
integer
>= 1
instanceWarmupSeconds
integer
<= 7200
schedule

Build on a schedule. Five-field cron in timeZone (default UTC). A missed window fires once (the next firing is computed from now). With onlyIfBaseChanged a firing is a recorded no-op when the resolved base is the image the last succeeded build used; a build already in flight is a no-op too. What the last firing did is on the recipe as lastScheduleAt / lastScheduleResult.

object
cron
required
string

Example

0 3 * * 1
timeZone

IANA zone name; default UTC.

string
onlyIfBaseChanged
boolean

The recipe.

Media typeapplication/json

An image recipe (irc-…).

object
ownedBy

Set on a platform-owned row; absent on a tenant’s own.

object
kind
required
string
Allowed values: auto-scaling-group platform
id
required

The owner: the Auto Scaling group id, or for platform the component (front-door, pack:platform-health, default-network, operator-recipe).

string
id
required
string
projectId
required
string
name
required
string
displayName
string
description
string
base
required

Where a build starts from. Exactly one of imageId, catalog, imageName, recipeId is set. imageName is a glob over the project’s own active images and recipeId chains to another recipe’s last succeeded output; both resolve to the newest match at build time.

object
imageId
string
/^img-[a-z0-9-]+$/
catalog

A /v1/images/catalog key (e.g. debian-13) — the active public image imported from it.

string
imageName

A glob over the project’s own image names (e.g. web-golden-*).

string
recipeId

Another recipe of the project; its last succeeded build’s output.

string
/^irc-[a-z0-9-]+$/
latest

Always true for imageName and recipeId at 2.0.

boolean
buildInstance
required

The throwaway build instance’s shape; it is launched in the recipe’s project and always terminated.

object
instanceType
required
string
subnetId

Omitted = the project’s default subnet.

string
/^subnet-[a-z0-9-]+$/
securityGroupIds
Array<string>
iamInstanceProfileId
string
/^iprof-[a-z0-9]+$/
keyPairName
string
steps
required
Array<object>

One provisioning step: exactly one of cloudInit (a cloud-config or shell document; every cloud-init step is concatenated into the build instance’s user-data and runs as root at first boot) or runCommand (run in order through run-command once the instance is up, as the agent’s login user). sudo: true runs a run-command step as root granted by the instance agent only on an instance carrying the stratum.dev/image-build tag it reads from IMDS, never on an ordinary instance, and never written to the guest.

object
name
required
string
cloudInit
string
runCommand
string
sudo
boolean
timeoutSeconds

Per-step deadline for a run-command step (default 600).

integer
>= 1 <= 600
tests
required
Array<object>

A run-command whose non-zero exit fails the build (no image is produced).

object
name
required
string
runCommand
required
string
sudo
boolean
timeoutSeconds
integer
>= 1 <= 600
output
required

The image a build produces.

object
namePattern

A Go template over {{.Recipe}}, {{.BuildNumber}}, {{.Date}} (YYYYMMDD); default {{.Recipe}}-{{.BuildNumber}}. Must render a valid image name.

string
description
string
visibility

Default private; public publishes the output (subject to the org’s public-images toggle); shared shares it with sharedWith — and publishes instead while none of those projects exist yet (the operator recipes name projects that arrive with /069).

string
Allowed values: private shared public
sharedWith

Project ids, with visibility: shared.

Array<string>
tags

AWS-shape resource tags. Freeform UTF-8 keys (1-128 bytes, no leading/trailing whitespace) mapped to freeform UTF-8 values (0-256 bytes). Max 50 tags per resource. Reserved prefixes (stratum.dev/, stratum.platform_health., stratum:, aws:) return 400 RESERVED_NAMESPACE on customer input. PATCH replaces the whole map..

object
<= 50 properties
key
additional properties
string
<= 256 characters
keepLast

After a successful build, retire the recipe’s older outputs beyond N (0 = keep all). What “retire” means is keepLastAction.

integer
keepLastAction

How keepLast retires an older output. deprecate (the default) hides it from default listings and launches keep working; disable refuses launches; delete deregisters it.

Only delete frees compute.images.count — the quota counts every image the project still owns, whatever its lifecycle state. A recipe that builds on a schedule and only deprecates will therefore fill its project’s image quota, and once it is full no build in that project can finish. The default stays deprecate so that upgrading never starts deleting images by itself; the seeded operator recipes ask for delete.

A delete is never forced: an image a launch template still names is left alone and the refusal is logged, so retiring cannot break a live Auto Scaling group.

string
default: deprecate
Allowed values: deprecate disable delete
tags

AWS-shape resource tags. Freeform UTF-8 keys (1-128 bytes, no leading/trailing whitespace) mapped to freeform UTF-8 values (0-256 bytes). Max 50 tags per resource. Reserved prefixes (stratum.dev/, stratum.platform_health., stratum:, aws:) return 400 RESERVED_NAMESPACE on customer input. PATCH replaces the whole map..

object
<= 50 properties
key
additional properties
string
<= 256 characters
distribution

The “bake → roll” half. After a successful build the builder creates a new version of each launch template with the output image (every other field copied from $Latest, setDefault as configured), then starts a instance refresh per group toward $Latest. Saving a recipe with launchTemplates needs asg:CreateLaunchTemplateVersion on each template and with instanceRefresh asg:StartInstanceRefresh on each group. A refresh refused (one already in progress) is recorded on the build as that target’s error; the build still succeeds and the version stays.

object
launchTemplates
Array<object>
object
launchTemplateId
required
string
setDefault
boolean
instanceRefresh
object
autoScalingGroupIds
required
Array<string>
minHealthyPercentage
integer
<= 100
maxBatchSize
integer
>= 1
instanceWarmupSeconds
integer
<= 7200
schedule

Build on a schedule. Five-field cron in timeZone (default UTC). A missed window fires once (the next firing is computed from now). With onlyIfBaseChanged a firing is a recorded no-op when the resolved base is the image the last succeeded build used; a build already in flight is a no-op too. What the last firing did is on the recipe as lastScheduleAt / lastScheduleResult.

object
cron
required
string
timeZone

IANA zone name; default UTC.

string
onlyIfBaseChanged
boolean
nextRunAt

The schedule’s next firing.

string format: date-time
lastScheduleAt
string format: date-time
lastScheduleResult

What the last firing did — built ib-… or skipped: ….

string
buildCount
required

Builds requested so far; the next build’s number is buildCount + 1.

integer
createdBy
required
string
createdAt
required
string format: date-time
updatedAt
required
string format: date-time

Example

{
"ownedBy": {
"kind": "auto-scaling-group"
},
"output": {
"visibility": "private",
"keepLastAction": "deprecate"
},
"schedule": {
"cron": "0 3 * * 1"
}
}

Bad request.

Media typeapplication/problem+json
object
type
required
string format: uri
title
required
string
status
required
integer
>= 400 <= 599
detail
string
instance
string format: uri-reference
requestId
required
string
code
required
string
issues

The findings of a rejected IAM policy document (custom and resource policies).

Array<object>

One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.

object
code
required

The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.

string
statement
required

Index of the statement at fault; -1 for the document as a whole.

integer
>= -1
detail
required
string
action

The offending action, where one is at fault (ACTION_NOT_APPLICABLE).

string
errors

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.

Array<object>

One field’s problem, the stage-2 installer API’s shape. field is the field’s path, message what is wrong with it.

object
field
required

The field’s path, e.g. links[0].interface, disks[1].confirm, mgmt.address or role; empty for the whole request.

string
message
required
string
details

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
key
additional properties
any

Example

{
"issues": [
{
"code": "ACTION_NOT_APPLICABLE",
"action": "compute:CreateVpc"
}
]
}

Authenticated but not authorized.

Media typeapplication/problem+json
object
type
required
string format: uri
title
required
string
status
required
integer
>= 400 <= 599
detail
string
instance
string format: uri-reference
requestId
required
string
code
required
string
issues

The findings of a rejected IAM policy document (custom and resource policies).

Array<object>

One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.

object
code
required

The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.

string
statement
required

Index of the statement at fault; -1 for the document as a whole.

integer
>= -1
detail
required
string
action

The offending action, where one is at fault (ACTION_NOT_APPLICABLE).

string
errors

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.

Array<object>

One field’s problem, the stage-2 installer API’s shape. field is the field’s path, message what is wrong with it.

object
field
required

The field’s path, e.g. links[0].interface, disks[1].confirm, mgmt.address or role; empty for the whole request.

string
message
required
string
details

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
key
additional properties
any

Example

{
"issues": [
{
"code": "ACTION_NOT_APPLICABLE",
"action": "compute:CreateVpc"
}
]
}

Conflicting state.

Media typeapplication/problem+json
object
type
required
string format: uri
title
required
string
status
required
integer
>= 400 <= 599
detail
string
instance
string format: uri-reference
requestId
required
string
code
required
string
issues

The findings of a rejected IAM policy document (custom and resource policies).

Array<object>

One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.

object
code
required

The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.

string
statement
required

Index of the statement at fault; -1 for the document as a whole.

integer
>= -1
detail
required
string
action

The offending action, where one is at fault (ACTION_NOT_APPLICABLE).

string
errors

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.

Array<object>

One field’s problem, the stage-2 installer API’s shape. field is the field’s path, message what is wrong with it.

object
field
required

The field’s path, e.g. links[0].interface, disks[1].confirm, mgmt.address or role; empty for the whole request.

string
message
required
string
details

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
key
additional properties
any

Example

{
"issues": [
{
"code": "ACTION_NOT_APPLICABLE",
"action": "compute:CreateVpc"
}
]
}

Request rate limit exceeded. The API-boundary throttle caps request throughput per principal and per project; code is RATE_LIMITED (distinct from the quota engine’s QUOTA_EXCEEDED). The Retry-After header carries the seconds to wait. Applies to any authenticated endpoint — it is enforced by middleware, not per-path.

Media typeapplication/problem+json
object
type
required
string format: uri
title
required
string
status
required
integer
>= 400 <= 599
detail
string
instance
string format: uri-reference
requestId
required
string
code
required
string
issues

The findings of a rejected IAM policy document (custom and resource policies).

Array<object>

One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.

object
code
required

The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.

string
statement
required

Index of the statement at fault; -1 for the document as a whole.

integer
>= -1
detail
required
string
action

The offending action, where one is at fault (ACTION_NOT_APPLICABLE).

string
errors

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.

Array<object>

One field’s problem, the stage-2 installer API’s shape. field is the field’s path, message what is wrong with it.

object
field
required

The field’s path, e.g. links[0].interface, disks[1].confirm, mgmt.address or role; empty for the whole request.

string
message
required
string
details

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
key
additional properties
any

Example

{
"issues": [
{
"code": "ACTION_NOT_APPLICABLE",
"action": "compute:CreateVpc"
}
]
}
Retry-After
integer

Seconds to wait before retrying.