Database migrations
Ordered, idempotent SQL applied under an advisory lock, so two builds racing on one database cannot half-apply a schema.
Migrations are plain SQL files in the repository, applied in filename order.
supabase/migrations/
20260726120100_extensions.sql
20260726120200_documents.sql
20260726120300_transactions.sql
…Two properties, both required
- Ordered, because a later migration may depend on an earlier one.
- Idempotent, because the pipeline runs on every build and preview and production share one database. Every statement is written to be safely re-runnable.
The advisory lock
The runner takes a Postgres advisory lock before applying anything, on a session-mode connection. Without it, two builds of the same commit — a preview and a production deploy triggered together — can interleave and leave a schema half-applied, which is the kind of failure that only reproduces under load.
Writing one
- Add the file with a timestamped name.
- Make every statement conditional or otherwise re-runnable.
- If it changes a row-level security policy, say why in a comment: those files are the security boundary, and a policy diff without a reason is unreviewable.
- Never edit a migration that has been deployed. Add another one.
Inspecting the result
It is your database. Open it with psql or the Supabase dashboard, look at the documents table, and read the policies. Nothing about the schema is hidden from you, which is the point of running your own.
