Skip to main content
Keep migrations in the directory selected by application inspection. Filenames use YYYYMMDDHHMMSS_name.sql: a 14-digit UTC prefix and lowercase descriptive name. Read ohmyhost init --dry-run --json for the exact source contract. Migrations are expand-only. Admitted statements create tables, types, domains, sequences, views/materialized views, triggers, indexes or SQL/PLpgSQL functions (SECURITY DEFINER requires SET search_path); ALTER TABLE … ADD, COMMENT ON, CREATE SCHEMA IF NOT EXISTS auth|extensions|private, CREATE EXTENSION IF NOT EXISTS pgcrypto|btree_gist|unaccent, and SET check_function_bodies=false are the other allowed forms. DROP/TRUNCATE, row changes, CREATE OR REPLACE, other ALTER, GRANT, DO and transaction control are refused. Text including comments cannot contain ohmyhost, supabase, policies/RLS, storage references, auth references other than admitted quoted Better Auth tables, or Supabase anon/authenticated/service_role authority. Init reports migration_sql_not_admitted with the refused file/reason. Existing tables gain only nullable columns without defaults and non-unique indexes. Constraints, foreign keys, triggers and unique indexes may attach only to tables created within the same pending migration run. All pending files apply atomically with a thirty-second statement and five-second lock timeout; CREATE INDEX CONCURRENTLY therefore fails. Applied files are immutable: never edit, rename or remove one; every new timestamp follows the applied catalog. Maximum 128 files,256 KiB each,2 MiB total, UTF-8/LF without BOM. Source planning checks canonical filenames, not complete SQL admission. Run init before paying for a build; execution/catalog validation may still fail as database_migration_failed, with none of the new files applied. Read DATABASE_MIGRATION_FAILED’s file and reason: MIGRATION_CHANGED_EXISTING_SCHEMA, MIGRATION_EXECUTION_FAILED, APPLIED_MIGRATION_CHANGED or MIGRATION_SQL_NOT_ADMITTED. Fix the cause and plan the new commit; never reset to repair a failed migration.

Make an additive change

Add a nullable field, deploy code supporting both shapes, then backfill with authorized database writes of at most 1,000 rows or bounded application work, outside migrations. Dropping/renaming columns and tightening existing constraints are unsupported; leave retired columns unused and enforce new rules in application code. Commit each versioned migration with the application code and matching lockfile. Before promotion, test the migration against representative existing records. A successful clean-database setup alone does not prove an upgrade preserves customer data.

Promote the schema

The promotion plan identifies the verified artifact and migration effects. Isolated Prod receives migrations, not Dev records. After applying, verify both the new feature and pre-existing Prod rows. In shared mode, both environments refer to the same data. Explain that impact before changing it. Do not restore a Dev dump into an existing Prod database to keep them synchronized. Dev and Prod · Database Skill · SQL exports.