ADR-004: Forward-Only Database Migrations
Status: Accepted Date: 2026-05-18 Sprint: SPRINT_20260518_055_Upgrade_graceful_drain_and_forward_only_migrations.md
This record states the platform’s official position on database schema rollback: Stella Ops migrations are forward-only, and the supported recovery path for a bad schema-changing upgrade is a PostgreSQL snapshot restore — not a reverse migration. Read it before authoring a migration or planning an upgrade-rollback procedure. Operators should also read the Migration Recovery Runbook.
Context
Stella Ops services apply PostgreSQL schema changes at startup through AddStartupMigrations(...) and the shared migration runner in src/__Libraries/StellaOps.Infrastructure.Postgres/Migrations/MigrationRunner.cs. That runner is forward-only by construction:
- SQL migration resources are applied once and tracked in each schema’s
__migrationstable. - There is no
Down(...)method. - There is no
down.sqlconvention. - There is no platform tool that attempts rollback by reverse migration.
The repository-wide startup migration requirement is documented in CLAUDE.md section 2.7. Manual postgres-init/ scripts are bootstrap fallbacks only; they are not the migration authority for running installations.
The upgrade-version-skew QA plan tracks this as TOPO-131. The honest recovery path for a bad schema-changing upgrade is a PostgreSQL snapshot restore, not a reverse migration.
Decision
Stella Ops database migrations are officially forward-only.
Operators MUST snapshot the PostgreSQL compose volume before every service upgrade that can apply database migrations. If a schema-changing upgrade must be rolled back, operators restore the pre-upgrade PostgreSQL snapshot and then start the earlier service binaries.
Schema-changing migrations MAY be additive-only, such as adding a table, column, or index. They MAY also be breaking when a release requires it. In both cases, the reverse path is the same: restore PostgreSQL from a pre-upgrade snapshot.
The executable recovery procedure is documented in Migration Recovery Runbook.
Consequences
Positive
- The migration model matches the existing production implementation.
- Operators get one explicit recovery path instead of a partial rollback story.
- QA can verify upgrade recovery by snapshot and restore instead of inventing unsupported down-migration expectations.
- Migration authors do not carry untested reverse scripts that can give a false sense of recoverability.
Tradeoffs
- Every release process must include a PostgreSQL snapshot step before services are upgraded.
- All services sharing the same PostgreSQL volume roll back together when that volume is restored.
- Rolling deploys need services to tolerate a briefly older schema where possible. Gateway version routing, including
DefaultRoutingPlugin.FilterByVersion, remains the control-plane guard for binary compatibility windows. - Compose-level blue/green deployment is feasible only when the shared schema change is additive-only. Sprint 056 owns that operator runbook.
Future Work
Any future downgrade-supporting feature is a new ADR. It must define the contract, tooling, test matrix, and operator evidence separately from this forward-only baseline.
Alternatives Considered
Down Migrations
Rejected. They double authoring cost, are hard to test against real production data, and often fail to reverse lossy changes. In this product, they would also conflict with the current startup migration runner.
Blue/Green PostgreSQL
Rejected for this baseline. Running two PostgreSQL clusters with bidirectional or cutover replication is out of scope for the self-hosted SMB target. Operators who need partial service rollback should use the Sprint 056 blue/green service runbook only when the database migration is additive-compatible.
References
- Migration Recovery Runbook
- Platform Architecture Overview
docs/qa/feature-checks/plans/2026-05-18-operator-trust/TIER_8_UPGRADE_VERSION_SKEW.mdsrc/__Libraries/StellaOps.Infrastructure.Postgres/Migrations/MigrationRunner.csCLAUDE.mdsection 2.7
