Flyway vs Liquibase: Choosing the Right Migration Tool

You are about to standardize a team on one migration tool, and the decision is not about XML versus SQL — it is about which tool’s failure modes you are willing to operate. Flyway and Liquibase diverge most where it costs you most: how each handles a transaction boundary on a failed migration, how each tracks checksums, and how each recovers from a locked history table after a CI/CD run dies. Pick wrong and you inherit either silent half-applied schemas or noisy checksum drift on every branch merge. This page compares the two on the axes that decide a zero-downtime deploy, and sits within the broader migration tool comparison section of the migration fundamentals guide.

Flyway vs Liquibase decision axes Two columns compare Flyway Community and Liquibase Open Source on transaction wrapping, rollback primitives, and checksum granularity, the three axes that drive operational risk. Where the Two Tools Diverge Flyway (Community) Liquibase (OSS) Txn wrapping manual BEGIN/COMMIT per-changeset, auto Rollback U-scripts / paid undo rollback <tag> built-in Checksum per-file CRC-32 per-changeset MD5 On MySQL neither tool can roll back DDL — the engine commits it implicitly.
The choice turns on transaction handling, rollback primitives, and checksum granularity — not on file format.

Symptom / Error Signatures

These signatures point to a tool-behavior mismatch rather than a bad script:

  • ERROR: lock timeout / deadlock detected during ALTER TABLE from a migration runner
  • FlywayException: Migration checksum mismatch or LiquibaseException: Checksum validation failed
  • flyway_schema_history or DATABASECHANGELOG held by an orphaned CI/CD session after a runner crash
  • PostgreSQL ERROR: cannot execute ALTER TABLE in a read-only transaction
  • Connection-pool exhaustion as a prolonged DDL blocks concurrent reads and writes

Root Cause Analysis

The divergence is rooted in how each tool manages the transaction boundary and the history table. Flyway Community does not wrap each migration in a transaction by default on most databases — a failed migration leaves the database partially altered unless you wrap the SQL yourself in BEGIN/COMMIT. Liquibase wraps each changeset in a transaction where the database supports it, at the cost of XML/YAML parsing and pre-flight validation overhead. On top of that, both tools track applied state in a history table, and concurrent pipelines competing for that table produce the lock contention you see in the logs. Both also surface checksum errors when legacy scripts lack conditional guards, which is why idempotent script design reduces churn regardless of tool. Crucially, the transactional advantage evaporates on MySQL, where DDL forces an implicit commit — the reason is detailed in handling non-transactional DDL in MySQL migrations.

Dimension Flyway (Community) Liquibase (Open Source)
Migration format Versioned SQL files SQL, XML, YAML, JSON changesets
Rollback support Manual U scripts; undo requires Teams/Enterprise liquibase rollback <tag> built-in
Checksum enforcement Per-file CRC-32; repair clears failed entries Per-changeset MD5; clearCheckSums forces recalc
Transactional wrapping Manual (BEGIN/COMMIT in your SQL) Per-changeset, configurable via runInTransaction
CI/CD integration Docker image, Maven/Gradle plugins, CLI Same, plus Spring Boot auto-configuration

Immediate Mitigation

Run as a privileged DBA or service account with pg_terminate_backend / MySQL PROCESS privilege. Halt all deployment pipelines first, and avoid peak traffic windows.

  1. Diagnose the blocking session holding the history table:
-- PostgreSQL · read-only diagnostic · run as a role that can see all sessions
SELECT pid, usename, state, query, wait_event_type, wait_event
FROM pg_stat_activity
WHERE (query LIKE '%flyway_schema_history%' OR query LIKE '%DATABASECHANGELOG%')
  AND state = 'active'
  AND pid <> pg_backend_pid();
-- MySQL · read-only diagnostic · run as a privileged user
SELECT * FROM information_schema.innodb_trx
WHERE trx_query LIKE '%DATABASECHANGELOG%';
  1. Terminate the orphaned lock — PostgreSQL SELECT pg_terminate_backend(<pid>);, MySQL KILL <trx_mysql_thread_id>;. Confirm the session is orphaned, not actively committing.
  2. Reconcile the history table. Flyway: flyway repair fixes checksums of failed migrations and removes failed entries. Liquibase: liquibase clearCheckSums forces recalculation on the next run.
  3. Reverse explicitly when needed. Flyway Teams/Enterprise: flyway undo -target=<previous_version>. Flyway Community: run pre-written inverse DDL wrapped in BEGIN; ... COMMIT;. Liquibase: liquibase rollback <tag_or_date>.

During reconciliation, route application traffic to a read replica or trip circuit breakers. Never force-reset a history table without a verified logical backup.

Permanent Fix / Long-Term Pattern

Remove lock contention and checksum drift with architectural controls rather than tool tuning. Decouple schema from app deploys with expand-and-contract: add columns and tables first, ship the code, drop legacy objects in a later release. Make transaction handling explicit — on Flyway, wrap DDL in BEGIN;/COMMIT; in your migration files (and remember PostgreSQL DDL is transactional while MySQL DDL is not); on Liquibase, set runInTransaction="false" for statements that cannot run inside a transaction, such as CREATE INDEX CONCURRENTLY. Enforce idempotent script design with IF NOT EXISTS guards and online-build flags (CREATE INDEX CONCURRENTLY on PostgreSQL, ALGORITHM=INPLACE, LOCK=NONE on MySQL). Add a pre-flight gate that runs flyway info or liquibase updateSQL against a staging replica before promotion, and enforce a single-runner policy so no two pipelines target the same history table at once. If a switch is on the table, plan it with migrating from Flyway to Liquibase without downtime.

Verification Checklist

  • BEGIN/COMMIT on Flyway, runInTransaction on Liquibase)
  • flyway validate / liquibase validate reports zero checksum mismatches across all environments
  • flyway_schema_history or DATABASECHANGELOG (verified via pg_stat_activity / innodb_trx)
  • CREATE INDEX CONCURRENTLY / ALGORITHM=INPLACE, LOCK=NONE) are present on every index migration

Frequently Asked Questions

Which tool should I pick if rollback matters most? Liquibase, because liquibase rollback <tag> is a first-class, built-in primitive in the open-source edition, whereas Flyway’s undo requires a paid tier and otherwise relies on hand-written U scripts. That said, on MySQL neither tool can truly roll back DDL — the engine commits it implicitly — so for MySQL the rollback story is your inverse-DDL discipline, not the tool.

Does Flyway really leave the database half-migrated on failure? On most databases the Community edition does not wrap a migration in a transaction by default, so a multi-statement script that fails partway leaves earlier statements applied. The fix is to wrap your DDL in explicit BEGIN;/COMMIT; (effective on PostgreSQL) and to keep every script idempotent so a retry converges.

How do I stop checksum mismatches on every branch merge? The mismatch usually means a migration file changed after it was applied. Treat applied migrations as immutable, add new files instead of editing old ones, and reserve flyway repair / liquibase clearCheckSums for genuine reconciliation rather than as a routine step in the pipeline.