Console
Sign in
Docs for Repository maintainer
On this page
  1. Before you start
  2. Steps
  3. 1. Read what the release changes
  4. 2. Check the mix you will run mid-rollout
  5. 3. Back up both databases
  6. 4. Apply the migrations
  7. 5. Roll the services in dependency order
  8. 6. Watch each step before the next
  9. 7. Roll back a service
  10. Check that it worked
  11. If something goes wrong
  12. Related

Upgrade and roll back

This guide moves a running deployment to a new platform release, one service at a time, and backs each service out again if the release misbehaves. It is for operators (sysops) and, for the migration steps, the database owner (dba).

Read byPlatform operatorDatabase administrator

Before you start#

What must be trueWhyReference
You know which release each service runs nowEvery /healthz answer carries the running version; platformctl version shows the CLI's and the deployment'sCLI spec §5.1
You have read the target release's changelogContract changes, migrations and deprecations are announced thereCHANGELOG.md
The target is a published releaseA release whose gates failed has its images withdrawn and publishes nothingPlatform release gates
You are within the support windowA release is supported while current and for one release after, so move from N to N+1Access support policy, SECURITY.md
Fresh backups of Access and Data exist and a restore was rehearsedData's data-changing migrations only roll forwardBack up and restore
A staging environment runs the current releaseAccess asks you to rehearse the rollback before you need itAccess deploy and roll back

Steps#

1. Read what the release changes#

The platform has one version line: the gateway, Access and Data ship together at the same MAJOR.MINOR.PATCH. A PATCH release only fixes, a MINOR release adds to contracts without changing them, and only a MAJOR release may change a contract, with each change and its migration listed in the changelog (gateway spec §14.2).

Check these points for the target release:

AreaRule to check againstReference
Route tableNew fields come with defaults; an unknown field is still a configuration errorgateway spec §14.2
Environment variablesA renamed variable keeps working under its old name, and logs a warning for one release before it goesgateway spec §14.2
DeprecationsAnnounced in the changelog, warned about in the log, removed in a MAJOR release at the earliestgateway spec §14.3
Data API and formatsAPI v1, access context 1, signature v5, manifest and policy bundle format 1; error codes are never removedData spec §13
Identity signatureOnly v5 exists since 1.0; a pre-1.0 gateway signs the removed v4Upstream identity: versions
Data legacy identityDATA_LEGACY_IDENTITY refuses v4 by default in 1.0.0, and 2.0.0 removes itLeave the legacy identity format

The 1.0.0 changes and their effect on each service are summarized in Data internals: platform release 1.0.0.

2. Check the mix you will run mid-rollout#

A rolling upgrade runs old and new versions side by side, and a deployment may keep them mixed. Which combinations are proven is the compatibility baseline: each release's baseline is the revision it shipped at, pinned in compatibility.json.

To prove the published images of the target release against your old ones, point the suites at running services instead of local builds:

bash
ACCESS_EXTERNAL_URL=https://access.staging.example.com \
ACCESS_EXTERNAL_ADMIN_TOKEN=… ACCESS_EXTERNAL_ACCESS_TOKEN=… \
  bun run compatibility

The variables are in live services and databases. Data stays unready while Access's discovery lists no access context version Data knows, which is why Access goes first in step 5 (Access spec §9.1).

3. Back up both databases#

Take a fresh Access dump and a fresh Data backup immediately before migrating. Access's older dump restores with its schema and catches up through the migration runner, while a database rollback of Data is not considered safe after a migration that changed data (Data spec §15.5). The commands are in Back up and restore.

4. Apply the migrations#

Each service treats its schema differently:

ServiceHow the new schema arrivesWhat to do
DataThe operator command, as the schema ownerRun migrate from the new release before its API replicas start; they stay unready until the ledger matches their migrations
AccessIts first new replica applies them at start, under an advisory lockNothing by hand; a binary needs the new archive's migrations/ beside it
Gateway (PostgreSQL state)Built on connect only when GATEWAY_POSTGRES_AUTO_MIGRATE=trueRun the new release's postgres:migrate if you build the schema yourself
bash
DATA_OWNER_DATABASE_URL=postgres://data_owner:…@postgres:5432/data \
  bun run --cwd packages/data migrate

migrate takes an advisory lock, applies the files in order and records each checksum in its ledger (migrations and drift). Never edit a migration that was already applied: the checksum mismatch is drift and fails readiness. If the changelog says a Data schema change breaks compatibility with the old release, drain the old Data replicas before you migrate. A destructive migration needs a reviewed plan and a restore you have tested.

On Kubernetes, run the new release's deploy/kubernetes/base/data/migrate-job.yaml by hand (Kubernetes manifests).

5. Roll the services in dependency order#

The docs fix no single upgrade order; this one follows the dependencies they do state. Promote the same digest from staging to production, and replace replicas one by one, gated on /readyz.

OrderServiceWhy hereRollout settings
1AccessData's readiness needs its access context discovery, and the gateway asks it for decisions10 s grace; wait for each old pod to be gone
2DataNeeds the new schema from step 4 and Access's contractGrace above DATA_DRAIN_MS (30 s)
3Upstreams of the gateway, if the signature format changesAn upstream must verify the new format before the gateway signs itPer upstream
4Gateway and its control planeLast of the services on the request path; the control plane validates tables with the same defaults as the gateway, so keep the two on one releaseGrace above GATEWAY_SHUTDOWN_TIMEOUT_MS; same secrets and route table
5ConsoleA client of the others; a restart signs everyone outRebuild from packages/console/Dockerfile at the new tag
6CLIInstall the new platformctl-<version>-<platform>.tar.gzCLI install

Keep each service's configuration and secrets unchanged during the upgrade, so a rollback only swaps the image. Do not write route-table fields that the new release adds until every gateway replica and the control plane run it, since an older replica refuses a table with a field it does not know. Data's manifests and policy bundles are versioned by content digest, so a new bundle is a release of its own: roll it the same way.

With the bundled Compose stack, change the image tags in deploy/compose/production/platform.compose.yaml; bun run deploy:check fails when image versions drift from the platform version (deploy rules).

6. Watch each step before the next#

WatchMeaningReference
/readyz of the new replicasThe new version accepted its dependenciesMonitor the platform
Access readiness 503 not-ready with an unknown migration in the start logThe database is ahead of the codeAccess start refusals
Data readiness reasonDrift, role or Access discovery problemsData 503 on readiness
gateway_config_reloads_total{result="failed"}The new gateway refuses the current route tableroute-table change has no effect
Access denial rate, login failures, decision latencyBehavior changed for real callersAccess monitoring
gateway_access_unavailable_totalThe gateway cannot use the new Access503 access_unavailable
Deprecation warnings in the logsSomething you use is scheduled for removalgateway spec §14.3

7. Roll back a service#

Images carry both the release tag and the version, so rolling back means deploying the previous release's digest with the configuration and secrets it ran with (platform rollback). Roll back in the reverse of the upgrade order when several services moved.

ServiceHowWhat survives, and the catch
GatewayDeploy the previous imageSessions survive while GATEWAY_SESSION_SECRET is unchanged, and upstreams keep verifying while the signing key is unchanged. Old pods drain: readiness 503, WebSockets close with 1001. Going below 1.0 brings back v4 signatures, which 1.0 upstreams refuse (gateway rollback).
Route tableplatformctl routes rollback <revision>, or POST /v1/config/rollback with a config tokenThe older table is written again as a new revision; history stays intact (changing the route table).
AccessRedeploy the previous digest, for example kubectl -n platform rollout undo deployment/platform-accessAccess operations call its migrations backward compatible for one release, but its spec also makes a replica refuse a database holding a migration it does not know. If the rolled-back replicas stay 503 not-ready naming an unknown migration, roll forward again or restore the pre-upgrade dump; rehearse this in staging.
DataRedeploy the previous imageIts schema stays where migrate left it: a rollback never reverts a migration, and a data-changing migration only rolls forward. Restore the pre-upgrade backup only for a destructive change, following the reviewed plan. Put the previous manifest and policy bundle back if those changed.
ConsoleRedeploy the previous buildEveryone signs in again.
CLIInstall the previous archiveProfiles and credentials are files and stay in place.
bash
docker pull ghcr.io/ggpwnkthx/access:<previous version>
kubectl -n platform rollout undo deployment/platform-access
kubectl -n platform rollout status deployment/platform-access

Check that it worked#

  1. Each /healthz reports the version you meant to run, and Data's also reports the manifest's schemaVersion:
bash
   curl -sS http://access:8081/healthz   # {"status":"ok","service":"access","version":"…"}
   curl -sS http://data:8082/healthz     # {"status":"ok","service":"data-backend","version":"…","schemaVersion":"…"}
   platformctl version
  1. Every replica answers 200 on /readyz; for Data that also proves the ledger matches the release's migrations.
  2. With store-mode route tables, GET /v1/replicas (or platformctl routes replicas) shows every gateway replica on the active revision with no last error.
  3. The Access administration smoke test passes: ACCESS_URL=… ACCESS_ADMIN_TOKEN=… bun run --cwd packages/access admin:smoke.
  4. A signed-in request through the gateway to /ws/{workspaceId}/v1/me returns the caller's identity from Data.
  5. The alerts from Monitor the platform stay quiet for a full day before you call the upgrade done.

If something goes wrong#

SymptomWhere to look
A new Access replica stays 503 and names an unknown migrationAccess start refusals
A binary Access finds no schemaAccess start refusals
Data readiness reports drift or a migration mismatchData 503 on readiness, migrations and drift
Data answers 503 AUTHORIZATION_UNAVAILABLE after the upgradeData 503 authorization unavailable, Access context refusals
Data or an upstream refuses the gateway's identity signatureData 401, gateway: identity signature refused
The new gateway refuses to startThe gateway refuses to start
Callers must log in again after a gateway rollbackRotating the session secret
Sessions break after an Access rollbackAccess token and JWKS problems