Skip to content

Database Migration Tools: Ptah, Atlas, Flyway, Liquibase, and Goose

Choose a database migration tool by its source of truth, review workflow, drift check, database coverage, and deployment model.

Ptah helps you plan, review, and apply database migrations. Try in your browser

A Go service needs a new PostgreSQL column. Its team wants to review the SQL before deployment, know which changes ran, and catch a column added outside the release process. Those are three separate jobs. A migration history can answer the second while the live database has already changed.

Ptah, Atlas, Flyway, Liquibase, and Goose all run database changes, but they start from different inputs and provide different checks. We use two small PostgreSQL journeys below to make the distinction concrete. The runnable files and full output are beside this guide.

Workflow map Start with the artifact What will the team review in Git?
01 / Review the input
  • Desired schema Ptah · Atlas

    Describe the target and generate a change. Both also run versioned migrations.

  • Ordered migration files Flyway OSS · Goose

    Review SQL and apply files in order. Goose can also run Go functions.

  • Conditional changelog Liquibase Community

    Select changesets by context or label and check preconditions.

02 / Apply the change
Each path changes a live database
03 / Check the result
  1. Migration history

    Which versions ran? Are files intact?

  2. Live schema drift

    Does the database match the source?

A clean history check does not answer the live-schema question.

Updated November 10, 2026. Each version below is the one used for the linked observation. Edition boundaries are part of the comparison. The runs are small fixtures, not deployment performance tests.

Tool Version Evidence used here
Ptah 0.25.0 PostgreSQL run in this guide and the 0.25.0 Atlas comparison
Atlas Community Edition, source-built 1.3.0 SQLite plan and apply run; edition matrix for Pro features
Flyway OSS 13.12.0 PostgreSQL migration and drift run
Liquibase Community 5.0.4 PostgreSQL changelog and drift run
Goose 3.29.0 PostgreSQL SQL migration run in this guide

Start with the input your team wants to maintain

Section titled “Start with the input your team wants to maintain”
Tool Primary input in the workflow discussed here What it supplies
Ptah A desired schema file or handwritten versioned SQL Planning, generated or handwritten migrations, and live comparison
Atlas A desired schema or a versioned migration directory Declarative and versioned workflows in CE; some review and drift operations depend on edition
Flyway OSS Ordered SQL migration files migrate and history validate
Liquibase Community A changelog of changesets, including formatted SQL update, update-sql, and changeset selection
Goose Ordered SQL files or Go migration functions up, down, and status through a CLI or Go package

The source of truth drives the choice. Ptah and Atlas can keep a desired schema and derive a change. Flyway OSS and Goose make the migration files the central artifact. Liquibase keeps a changelog with identity, selection, and conditional execution rules that can be more important than the SQL alone. Flyway’s commercial editions add schema comparison and generation; the command tiers identify which edition supplies each operation.

Ptah: keep a target schema or write migrations

Section titled “Ptah: keep a target schema or write migrations”

Ptah has a direct schema path and a versioned migration path. The direct path compares a desired file with a database and can save a plan whose exact bytes are signed before apply. The versioned path can generate up and down SQL against a disposable shadow database, or run SQL the team wrote. It records revisions and can compare the live schema with a release’s desired file. The signed direct-plan approval does not gate an ordinary migrations up; use the appropriate deployment policy for that path.

Atlas: check which edition supplies the workflow

Section titled “Atlas: check which edition supplies the workflow”

Atlas CE can inspect, diff, and apply a desired schema, and it can run versioned migrations. In our source-built CE 1.3.0 run, schema apply --dry-run previewed a SQLite column, while schema plan was unavailable. The Atlas feature matrix assigns saved planning, drift detection, and managed rollout features to paid tiers. Its Kubernetes Operator supports desired-schema and versioned resources. A source-built CE binary, the standard download, and a Pro-connected deployment have different terms and commands; pin the one your team will use.

Flyway OSS applies ordered migration files and stores their checksums in a schema history table. Its validate command checks that history against the files. In our PostgreSQL run, it rejected an edited applied migration, while a column added outside Flyway left validate green. The command tiers place a deployment dry run and undo in Teams, and schema drift detection in Enterprise. A team with a working Flyway pipeline can keep its runner and add a separate read-only schema check.

Liquibase: keep conditions with the change

Section titled “Liquibase: keep conditions with the change”

A Liquibase changeset has an id, an author, and a changelog path as its identity. Community can preview SQL with update-sql before update. The changelog can also carry contexts, labels, preconditions, and runAlways. Contexts and labels filter changesets when the deployment supplies a matching filter. Preconditions check the database state, while runAlways reruns a changeset on each update. That policy travels with the changelog; a plain ordered SQL directory does not preserve it. Our Community 5.0.4 run also showed that diff reported a manual column by comparing two databases, but returned 0. A pipeline that needs a failing drift gate must handle that report or choose a different check.

Goose: keep migrations near the Go program

Section titled “Goose: keep migrations near the Go program”

Goose accepts SQL files and registered Go migration functions. Its Go API can apply SQL embedded in an application binary; create and fix still work on files. The measured CLI path below used a migration directory, database URL, and up/status. After a manual schema change, status continued to report the two applied versions. We added Ptah as a read-only check of the desired schema; Goose still owned execution and history.

A Go developer: keep Goose, add a live check

Section titled “A Go developer: keep Goose, add a live check”

Goose fits a service that already owns SQL migrations and wants a small Go runner. Its Go package can also apply embedded SQL files. We used its CLI and SQL files here; an embedded runner is a separate integration choice.

The first migration creates public.products and four sample rows. The next adds a reorder threshold:

examples/goose/migrations/00002_reorder_level.sql
-- +goose Up
ALTER TABLE public.products
ADD COLUMN reorder_level integer NOT NULL DEFAULT 5;
-- +goose Down
ALTER TABLE public.products DROP COLUMN reorder_level;

With the disposable database and connection variable from the setup, we applied version 1, inserted the rows, and then ran the pending migration:

Terminal window
goose -dir goose/migrations postgres "$GOOSE_DB_URL" up-to 1
goose -dir goose/migrations postgres "$GOOSE_DB_URL" up
goose -dir goose/migrations postgres "$GOOSE_DB_URL" status

Goose recorded both versions. The resulting rows were:

examples/measured/goose-rows.txt
id,sku,stock,archived,reorder_level
1,KIT-42,12,f,5
2,CABLE-7,3,f,5
3,ADAPTER-2,0,f,5
4,OLD-KIT,8,t,5

Now the question changes. Does the live public schema still match the release? We used the desired schema file as a read-only Ptah check beside Goose. The Goose history table is excluded because it is not an application table:

Terminal window
ptah schema drift --db-url "$GOOSE_DB_URL" --schema-file schema.sql --schemas public --exclude public.goose_db_version

It first returned 0 with No schema drift detected. We then added manual_note outside the migrations. Goose status still showed both versions as applied. The same Ptah command returned 1:

examples/measured/goose-ptah-drift.excerpt.txt
Findings:
- columns_removed: 1 (destructive)

The label describes what reconciliation to the file would remove; the check did not remove the column. Goose remains the migration runner in this arrangement. Ptah adds a separate schema check, which needs a schema file matching the release currently deployed. This example does not import or rewrite Goose history. Our Flyway comparison shows the same history-versus-live-schema distinction with Flyway OSS.

Check on goose_lab Before manual column After manual column
Goose status Both versions applied; exit 0 Both versions applied; exit 0
Ptah schema drift No drift; exit 0 columns_removed; exit 1
Field observation One change, two signals Recorded on PostgreSQL 18.6
01 / Change outside migrations
Outside the migration runner Add public.products.manual_note
02 / Run both checks
Goose status 2 applied migrations

History is current exit 0

Ptah schema drift columns_removed: 1

Live schema changed exit 1

Review the column before reconciling. The checks report different facts about the same database.

The second result is a report about the difference, not an instruction to drop the column. The release owner must decide whether to preserve the manual change or amend the desired file. A CI job should fail and ask for that decision before executing any reconciliation SQL.

A team deployment: review generated SQL before apply

Section titled “A team deployment: review generated SQL before apply”

Another team wants a schema file in Git to describe the target, generated SQL to review in a pull request, and a check after deployment. We used the same starting table and rows in a separate database. In schema.sql, the new column appears as part of the desired table:

examples/schema.sql
CREATE TABLE public.products (
id bigint PRIMARY KEY,
sku text NOT NULL UNIQUE,
stock integer NOT NULL DEFAULT 0,
archived boolean NOT NULL DEFAULT false,
reorder_level integer NOT NULL DEFAULT 5
);

The setup applies version 1 and seeds the table. Generation uses a separate disposable shadow database. Ptah resets that database and replays the migration up, down, and up while checking the files:

Terminal window
ptah migrations generate --db-url "$PTAH_DB_URL" --schema-file schema.sql --schemas public --migrations-dir ptah/migrations --name reorder_level --shadow-db "$PTAH_SHADOW_URL"
ptah migrations hash --dir ptah/migrations
ptah migrations up --db-url "$PTAH_DB_URL" --migrations-dir ptah/migrations --verify-sum --dry-run
ptah migrations up --db-url "$PTAH_DB_URL" --migrations-dir ptah/migrations --verify-sum
ptah schema compare --db-url "$PTAH_DB_URL" --schema-file schema.sql --schemas public --exit-code

Reviewers can read both generated files and the dry run before the apply step. The up file contained:

examples/measured/ptah-up.excerpt.sql
ALTER TABLE "public"."products" ADD COLUMN "reorder_level" integer NOT NULL DEFAULT 5;

The down file drops the column. It would also lose values written there, so a successful shadow replay is not a recovery plan for production data. In our run, the apply recorded the new version, the final comparison reported no schema differences, and the four application rows matched the Goose path. The full dry run, generated files, and output show the exact sequence.

Stage What the team sees What the command enforces
Generate Desired schema and generated up/down files Shadow replay checks that this pair runs on the disposable fixture.
Review The SQL diff in a pull request The pull request process records human review; Ptah has not applied the SQL.
Preview One pending version and its SQL in the dry run --dry-run makes no database change.
Apply The reviewed migration directory --verify-sum checks the directory integrity; Ptah records the applied version.
Compare No schema differences --exit-code fails if the live database differs from the desired file.

A pull request review is a team process around these versioned files. It is not a Ptah signature gate on migrations up. If approval must be enforced by the tool, Ptah has a signed saved-plan flow for direct schema changes. Its Kubernetes Operator workflow has a separate plan approval policy. Atlas also has a Kubernetes Operator for declarative and versioned resources. Compare the actual deployment path and edition before treating those controls as interchangeable.

Migration history and live schema are different evidence. Our Flyway OSS run found that validate rejected an edited applied file while an out-of-band column left validate green. Flyway’s check -drift is in Enterprise. In our Liquibase Community run, status and validate likewise stayed green after a manual change. Community’s diff compared two databases and reported the column, but exited 0. Ptah’s file-to-database drift command returned 1 in the Goose journey above. Atlas’s edition matrix separates CE inspection from its Pro drift workflow. Those commands do not all check the same inputs or provide the same CI signal.

Schema inspection answers another question: what objects exist now? Ptah’s schema inspect and Atlas’s schema inspect export live definitions. Flyway info and Goose status in our runs reported migration history. Liquibase Community diff compared two live databases. Match the command to the evidence you need.

Database names alone are not a support guarantee. Check the object types and operations you need in the Ptah support matrix, Atlas edition and database matrix, Flyway database matrix, Liquibase supported databases, and Goose dialect list. PostgreSQL row-level security shows why the object list matters. Ptah’s PostgreSQL model includes policies. Atlas lists RLS among its Pro-only PostgreSQL objects. Our Liquibase Community fixture could apply SQL affecting RLS, but its diff did not report the tested policy change. A runner that executes SQL and a tool that models the resulting object offer different coverage.

The extra infrastructure depends on the chosen job. Ptah generation needs a disposable shadow database, while its handwritten migration path does not. Atlas migration generation uses a dev database. Liquibase Community 5.0.4 needed a PostgreSQL driver installed in our measured setup. Goose’s SQL path used a directory, a connection, and its binary. A Ptah or Atlas Operator adds a controller and Kubernetes resources when that reconciliation model is useful. None is required for the CLI journeys here.

Licenses also differ. Ptah and Goose use MIT. Atlas CE and the Flyway open-source project use Apache 2.0; their downloadable or paid features have separate terms. Liquibase Community 5.0.4 uses FSL-1.1-ALv2. Check the edition and license of the binary you deploy, not only the repository name.

Need Start here Decision boundary
Desired schema, reviewed SQL, and an open-source live drift gate Ptah Check the engine’s object coverage and the shadow database needed for generation.
Desired schema with Atlas Cloud, its registry, or managed review workflows Atlas CE and Pro differ; pin the edition.
A working versioned SQL pipeline with Flyway history Flyway Keep it when switching runners adds risk; compare its paid checks if drift is the missing job.
Conditional or repeatable changesets across environments Liquibase Contexts, labels, preconditions, and runAlways are distinct from a plain migration directory.
SQL or Go migrations close to a Go application Goose Add a separate live-schema check if the application needs one.

For a Go application that previously changed its schema on startup, our GORM migration case follows the move to reviewed SQL. The wpmgr case shows why object coverage, including an RLS policy, can decide an Atlas migration. For Kubernetes delivery, the OCI-to-plan walkthrough shows the separate publication, approval, and apply steps.

Ptah 0.25.0 is pre-GA. Pin its release and check CLI changes during upgrades.

Can I use a drift checker without changing migration runners?

Section titled “Can I use a drift checker without changing migration runners?”

Yes. The Goose journey did exactly that. Keep the runner’s history table out of the desired application schema, or exclude it from the comparison. Use the schema file for the release already deployed before an upgrade, then the new file after applying it. Otherwise the planned change looks like drift.

No. A dry run previews SQL. It does not identify who approved those bytes. Use your pull request process for versioned files, or choose a workflow with an enforced approval object or signature when that is a requirement.

Should I move an existing changelog to Ptah?

Section titled “Should I move an existing changelog to Ptah?”

Only after checking what the changelog means. Our Liquibase comparison shows that Ptah refuses import of changesets with contexts, labels, preconditions, or runAlways; silently dropping those conditions would change deployment behavior. The Flyway handoff and Atlas handoff follow different histories. A team can also keep its existing runner and add a read-only Ptah check first.

Put Ptah to work

Example files