In-Place Upgrades
When the template a stack was launched from has been updated to a new version, you can adopt those changes without tearing the stack down. The upgrade engine diffs the running stack's snapshotted descriptor against the latest template version, classifies each resource's delta, and applies only the changes that are safe to make non-destructively.
How the engine classifies changes
Every resource in the diff receives one of three classifications:
| Classification | Meaning |
|---|---|
unchanged | The resource's spec is identical in both versions. The engine does not touch it. |
in_place | A safe, non-destructive change the engine can apply to the running resource. |
blocked | A change that would recreate, shrink, or fundamentally alter a stateful resource. A fresh stack is required. |
If any resource is blocked, the entire upgrade is blocked. The engine will not apply the in_place changes either. You must launch a new stack to adopt those changes.
What can be upgraded in place
The set of spec keys the engine treats as in_place differs by resource kind:
| Kind | In-place keys | Action label |
|---|---|---|
app | image_ref, env, auth, domain | redeploy_app |
app | plan_name | scale_app |
database | plan_name (compute resize) | scale_service |
inference | (none; any change is blocked in the current version) | — |
files | (none; any change is blocked in the current version) | — |
Changes to any other spec key on any kind are blocked. The intent is conservative: the engine cannot recreate or shrink a stateful resource (a database or a bucket) in place, so those paths are entirely refused.
What is always blocked
The following changes are always blocked, regardless of kind:
- Adding a resource. A new resource in the target template has no running counterpart. Adding it during an upgrade is not supported; launch a fresh stack.
- Removing a resource. Removing an existing resource would leave no path to tear it down gracefully within the upgrade. Launch a fresh stack.
- Changing a resource's kind. For example, changing a
databaseresource to afilesresource. - Changing a database engine, version, storage size, or storage tier. Any key on a
databaseresource other thanplan_nameis blocked. - Any change to an
inferenceorfilesresource. Support for in-place inference key reminting and files quota adjustment is planned but not yet available.
Step 1: Preview the upgrade
Always preview before applying. The preview computes and returns the classified plan without changing any state:
curl -X POST https://api.foundrydb.com/stacks/$STACK_ID/upgrade/preview \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"
Example response for an unblocked upgrade:
{
"from_version": "1.0.0",
"to_version": "1.1.0",
"changes": [
{
"symbolic_name": "db",
"kind": "database",
"change": "unchanged"
},
{
"symbolic_name": "app",
"kind": "app",
"change": "in_place",
"action": "redeploy_app",
"target_spec": {
"attachment_kind": "open-webui",
"image_ref": "ghcr.io/open-webui/open-webui:0.4.5"
}
}
],
"new_monthly_cost": 87.00,
"current_monthly_cost": 87.00,
"cost_delta": 0.0,
"blocked": false,
"blocked_reasons": []
}
Example response for a blocked upgrade:
{
"from_version": "1.0.0",
"to_version": "2.0.0",
"changes": [
{
"symbolic_name": "db",
"kind": "database",
"change": "blocked",
"reason": "changing \"version\" on a database resource requires a fresh stack (it could recreate or resize stateful data)"
},
{
"symbolic_name": "app",
"kind": "app",
"change": "in_place",
"action": "redeploy_app"
}
],
"blocked": true,
"blocked_reasons": [
"db: changing \"version\" on a database resource requires a fresh stack (it could recreate or resize stateful data)"
]
}
When blocked is true, no upgrade path is available. Launch a new stack from the updated template and migrate your data manually.
Step 2: Apply the upgrade
Once you have reviewed the plan and accepted the new monthly cost:
curl -X POST https://api.foundrydb.com/stacks/$STACK_ID/upgrade \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"accepted_monthly_cost": 87.00}'
Response semantics:
| HTTP status | Meaning |
|---|---|
202 Accepted | Upgrade accepted. The reconciler is applying changes. Response body is a StackMigration record. |
200 OK with {"status":"up_to_date"} | No changes to apply. The stack is already on the latest template version. |
409 Conflict | An upgrade is already in progress, or the accepted cost drifted from the current estimate by more than $0.01. Re-preview and resubmit. |
422 Unprocessable Entity | The plan is blocked. Launch a fresh stack to adopt these changes. |
Monitoring the upgrade
While the upgrade is in progress, the stack's status moves to Upgrading. The StackMigration record returned by the 202 response tracks the upgrade's lifecycle:
| Migration status | Meaning |
|---|---|
Accepted | Upgrade accepted, reconciler not yet started. |
Applying | The reconciler is applying in-place changes. |
Completed | All changes applied. The stack is back to Running. |
Failed | A single step failed (see below). |
Retrieve the stack to see the current status:
curl https://api.foundrydb.com/stacks/$STACK_ID \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"
Failure behaviour
A failed upgrade rolls back only the in-flight change and returns the stack to Running. It never triggers a full stack teardown. Your data and the other running resources are unaffected.
The migration record's status moves to Failed and status_detail describes what went wrong. After reviewing the migration record, you can re-preview and reapply once you have resolved the issue, or leave the stack on its current template version.
Cost gate
The upgrade endpoint enforces the same cost gate as the launch endpoint. accepted_monthly_cost must be within $0.01 of the estimate returned by the upgrade preview. If the template's resources have changed price between preview and apply, you receive 409 Conflict and must re-preview.
Deciding between upgrade and a fresh stack
Use in-place upgrade when:
- The only changes are app redeployments (new image version) or compute plan resizes.
- You want zero-data-migration path.
Launch a fresh stack when:
- The upgrade preview returns
blocked: true. - The template adds or removes resources.
- The database engine, version, storage size, or storage tier changes.
- You want a clean-slate environment before migrating traffic.