Files
Gebos/docs/adr/0001-manual-supabase-cli-migrations.md
T
Lars Nolden 2ce68cb098
deploy / deploy (push) Successful in 5m57s
check / flake-check (push) Failing after 35m52s
Wire schema v1 into a manual Supabase migration and vendor the real stack
- Move db/schema.sql to supabase/migrations/ as the first supabase CLI
  migration (manual `db push` only, no automated runner); re-add the
  gebos_ingest role + grants there since init.sql never re-runs
- Add gebos-postgres-passwords oneshot on db-host: syncs role passwords
  from sops (LoadCredential, journal-safe), makes supabase_admin
  SUPERUSER and hands the auth schema to supabase_auth_admin to match
  the upstream supabase/postgres image; add pg_hba rule for 10.0.0.0/8
- Vendor the official docker-compose (studio/kong/auth/rest/meta only,
  external Postgres, loopback Studio with no Kong dashboard route) plus
  kong.yml (trimmed) and kong-entrypoint.sh (verbatim); tested: compose
  config, Kong config parse, migration applied on TimescaleDB pg17
- Document decisions as ADRs 0001-0004 (migrations, passwords, vendored
  stack, JWT API keys incl. verify/mint procedure)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 22:33:50 +02:00

2.3 KiB

ADR-0001: Database schema changes are manual Supabase CLI migrations

Date: 2026-07-08 Status: Accepted

Context

Schema v1 (db/schema.sql, ~600 lines) was committed but wired to nothing. The only SQL that ran automatically was nix/supabase/init.sql via services.postgresql.initialScript — which executes only at first initdb, so on the already-initialized db-host cluster neither it nor any schema change would ever apply again. A delivery mechanism was needed.

Options considered:

  1. Automated runner — a systemd oneshot on db-host applying migrations on every deploy (dbmate/atlas or plain psql with a tracking table).
  2. Manual Supabase CLI migrations — plain-SQL files in supabase/migrations/, applied by a human with supabase db push.
  3. Extending initialScript — rejected outright: never re-runs on an existing cluster.

Decision

Option 2. Schema changes are rare, deliberate, admin-level operations; a human applies them on purpose, and no automation exists to break or to apply a half-reviewed migration as a side effect of a deploy.

  • Migrations live in supabase/migrations/<timestamp>_<name>.sql; schema v1 moved there as the first one. supabase/config.toml is the minimal CLI project marker.
  • Applied via SSH tunnel to db-host as the postgres superuser (see supabase/README.md for the exact commands). The CLI records applied files in supabase_migrations.schema_migrations, so pushes are incremental and re-running is safe. Individual migrations need not be idempotent.
  • Layering rule: nix/supabase/init.sql holds only cluster bootstrap (Supabase schemas, admin/API roles, extensions needing shared_preload_libraries); everything else — including the gebos_ingest role and its grants, which are coupled to application tables and must reach existing clusters — lives in migrations. Passwords live in neither (see ADR-0002).

Consequences

  • Deploys (deploy-rs) never touch the schema; a deploy and a migration are two separate, independently reversible acts.
  • A human must remember to push after merging a migration. Accepted for a pilot with one operator.
  • The local nix run .#dev Postgres has no TimescaleDB yet, so the v1 migration's create_hypertable fails there until the dev stack gains the extension.