Atlas · versioned migrations · PostgreSQL 13+ · GitHub Actions

Test Atlas migrations against production before you merge

Atlas replays your migrations on a dev database to lint them. DbProof runs atlas migrate apply on a copy of production's schema, with its row statistics, on every pull request, and says what would fail or lock in production.

How it works with Atlas

Real atlas migrate apply, against production's shape

Connect a repository and DbProof finds the folder with atlas.sum and opens a pull request with the workflow.

Restore production's shape

The check starts a throwaway Postgres on production's major version in your runner and restores the latest snapshot of production's schema and atlas_schema_revisions.

Apply one version at a time

Atlas runs with --to-version for each new migration in turn, so a finding names the file that caused it.

Weigh it against production

DbProof compares the result with production's statistics and reports on the pull request as a check and a comment, with the file, the line and a suggested fix.

The setup pull request installs Atlas and points it at the check's database:

- uses: ariga/setup-atlas@v0
- uses: dbproof/check-action@v1
  with:
    project: your-org/ledger
    migrations-dir: migrations
    migrate-command: atlas migrate apply --dir "file://migrations" --url "$DBPROOF_CHECK_DSN"
Findings

What the check reports

Each finding names the migration file and line, says what production would do, and suggests a fix. Errors fail the check; warnings don’t.

error

NOT NULL on a column with nulls

Production's statistics show nulls, so SET NOT NULL fails mid-deploy.

error

Migration fails to apply

A hotfix already added the column in production. The migration replays cleanly on the dev database and fails on deploy.

warning

Index built without CONCURRENTLY

Writes to a large table blocked while the index builds. The fix builds it concurrently, in a file Atlas runs outside a transaction (-- atlas:txmode none).

warning

Table rewrite on a table over 1M rows

A type change that rewrites the whole table while holding its lock.

warning

Foreign key without an index

Every delete from the parent scans the child table.

warning

Dropped column

Code still reading it breaks the moment the migration runs.

error

A stale atlas.sum

Atlas refuses to run a folder whose sum doesn't match its files. The check says so, and to run atlas migrate hash.

drift

Production changed outside migrations

DbProof opens a pull request with a migration that adopts the change and an updated atlas.sum.

What leaves production

Structure and statistics, never rows

A capture step in your deploy job reads production with the connection string your migrations already use, and uploads a snapshot.

What capture reads

  • The catalog: tables, columns, indexes, constraints, views and functions
  • Planner estimates: row counts, and each column's share of nulls, distinct values and width
  • atlas_schema_revisions, in Atlas's own schema or, as on Neon, in public

What it never reads

  • Rows from any of your tables
  • Sample values: no most-common values, no histograms
  • Your connection string: it stays in your GitHub secrets
FAQ

Atlas questions

Declarative mode, atlas schema apply?

Not yet. DbProof tests versioned migrations: the files in your migrations folder, applied with atlas migrate apply.

How is this different from atlas migrate lint?

Lint analyzes your migrations on a dev database. DbProof applies them to a copy of production's schema and weighs them against production's statistics, so it knows that a column already has nulls, that a table has 12 million rows, or that production changed outside your migrations. They work well together.

Does DbProof connect to my database?

No. Capture runs in your deploy job, and checks run against a throwaway Postgres in your runner. DbProof only receives the snapshot.

Find out what your next Atlas migration does to production

Connect a repository in a few minutes. Free during the beta.