On this page

Rollback on MIOSA is fast because nothing has to be rebuilt. The previous release is still in storage. Runtime instances boot quickly. Static artifacts are streamed from object storage. The whole operation is “change one row, propagate to the edge cache.”

When you’d rollback

  • New version passed health checks but the app is broken in real traffic (e.g. a feature flag mis-fire).
  • A bad migration deployed.
  • A regression you missed in staging.
  • Hostile incident — quick revert while you investigate.

How fast

StepStaticDynamic
Update active version<1s<1s
Edge route cache refresh<60s default<60s default
New runtime instances spawn?n/a (no per-app process)Only if older version’s instances were torn down; the scheduler boots replacements
Drain old runtime instancesn/aOnly if rolling forward, not on rollback
Total wall clocksecondsseconds to ~1 minute

If the older version’s runtime instances are still healthy (recent rollforward where the old instances haven’t been torn down yet), dynamic rollback is also instant — the router switches backends without any VM boots.

How rollback works

API

Server behavior:


1. Validate target version_id belongs to this deployment
2. Validate target version state === "ready" (cannot rollback to failed)
3. Validate target version's release artifact still exists in storage
   (returns 410 Gone if GC'd)
4. UPDATE deployments SET active_version_id = $version_id
5. Invalidate edge route cache
6. For dynamic: confirm enough healthy instances for the old version; spawn if needed
7. Return the deployment with the new active_version_id

Response:

{
  "data": {
    "id": "dep_...",
    "active_version_id": "ver_01H...",
    "state": "running",
    "public_url": "https://smile-dental.cliniciq.miosa.app"
  }
}

Picking the rollback target

Use the Versions API to inspect candidates:

const versions = await miosa.deployments.versions.list(deploymentId, {
  state: "ready",
  limit: 10,
})

// Find the last known good version
const target = versions.data.find(v => v.id !== deployment.active_version_id)

For production, picking the immediately-previous version is usually correct. For incidents, you may need to go further back if multiple recent versions share the bug.

Rolling forward after rollback

After a rollback, the version you rolled back FROM is still in ready state (unless you explicitly archive it). You can re-promote it later if it turns out the issue wasn’t in the code:

await miosa.deployments.versions.promote(deploymentId, originalVersionId)

This is a regular promotion, not a rollback. The path is symmetric.

What rollback doesn’t fix

IssueWhy rollback doesn’t help
Bad database migrationThe database is outside the version. Roll back the schema separately or restore from backup. See Postgres.
Bad managed-data stateSame — data services persist across version changes by design.
External service config driftStripe webhooks, OAuth callbacks, etc. — not part of the release.
Already-served bad responsesAlready in your customers’ browsers / caches. Bust caches separately.

Rollback fixes code; it does not fix state. Plan migrations to be backward-compatible (expand → migrate → contract) so older versions keep working after a forward migration.

Idempotency

Rollback requests carry an Idempotency-Key header. Identical keys within the retention window return the original result without re-running the operation. This matters during incidents when retry storms are common.

Audit

Every rollback emits an audit event with:

  • The previous active_version_id
  • The target version_id
  • The user / API key that performed it
  • The Idempotency-Key
  • External attribution

Audit events are queryable through the platform audit API and are retained per the tenant’s plan.

See also

Was this helpful?