flyway-migrations
Authors and runs Flyway database migrations - versioned (`V1__add_users.sql`), repeatable (`R__refresh_views.sql`), and undo (`U1__remove_users.sql`) migration files in `db/migration/`; runs `flyway migrate` / `info` / `validate` / `clean` / `baseline` / `repair`; tracks state in the `flyway_schema_history` table; supports 50+ databases including Oracle / SQL Server / MySQL / PostgreSQL / MariaDB / Snowflake / BigQuery; integrates with Maven, Gradle, CLI, and Docker. Use when the user works with Flyway-managed schemas, asks about migration ordering, or needs CI gates on schema changes.
Install with skills.sh (any agent)
npx skills add testland/qa --skill flyway-migrationsflyway-migrations
Overview
Flyway tracks applied migrations in a per-database flyway_schema_history table and applies pending migrations in order by version number (fw-how (opens in new window)).
When to use
How to use
Follow Steps 1 - 7 below in order; each numbered step is the single source for that part of the workflow.
Step 1 - Install
Flyway runs on Windows, macOS, Linux, and Docker, plus Maven and Gradle plugin distributions (fw-home (opens in new window)). Common install paths:
# Docker (zero-install for CI)
docker run --rm flyway/flyway -url=jdbc:postgresql://host/db -user=usr -password=pwd migrate
# Homebrew (macOS / Linux)
brew install flyway
# Maven plugin (Spring Boot etc.)
# add to pom.xml under <build><plugins>Step 2 - First migration
Migrations may be written in SQL, Java, or other scripting languages (fw-how (opens in new window)). File naming places migrations in the configured locations (default db/migration):
db/migration/
├── V1__create_users.sql
├── V2__add_email_index.sql
├── R__refresh_active_users_view.sql # repeatable, reruns on checksum change
└── U1__remove_users.sql # undo (Flyway Teams)The prefix scheme:
| Prefix | Type | Reruns? | Use |
|---|---|---|---|
V<n>__ | Versioned | Once | New schema changes; immutable after merge |
R__ | Repeatable | When checksum changes | Views / stored procs / seed data |
U<n>__ | Undo | Inverse of versioned | Rollback (Teams edition) |
__ (double underscore) separates version + description; .sql (or configured suffix) marks the file as a migration.
Step 3 - Core commands
The daily loop is flyway info (preview pending) -> flyway migrate (apply pending) -> flyway validate (checksum-verify applied files before deploy). After a failed migration, flyway repair fixes flyway_schema_history - never edit an applied file. Full command reference (baseline, undo, clean, and the rest): references/commands.md.
Step 4 - Pending-migration semantics
Migrations with a version lower than the history table's current version are ignored by default; the rest are pending - available but not applied (fw-how (opens in new window)). Safety property: a developer who pulls main and runs flyway migrate applies only the new migrations; those already in flyway_schema_history are not re-run.
Step 5 - Configuration
Configuration via flyway.conf file, env vars (FLYWAY_*), or CLI flags. Key settings:
flyway.url=jdbc:postgresql://localhost:5432/mydb
flyway.user=myuser
flyway.password=mypass
flyway.locations=filesystem:db/migration,classpath:db/migration
flyway.baselineOnMigrate=true # auto-baseline empty schemas
flyway.cleanDisabled=true # CRITICAL for prod - disable destructive `clean`
flyway.outOfOrder=false # reject migrations with versions lower than max applied
flyway.validateOnMigrate=true # checksum-validate before applyingcleanDisabled=true is a mandatory production guard - flyway clean drops every object in the schema. Always set this in production config; only enable for ephemeral test databases.
Step 6 - CI integration
Gate every PR on an ephemeral DB (Docker / Testcontainers): spin the DB, apply migrations, run tests against the migrated schema. The full GitHub Actions job and the Testcontainers @BeforeAll pattern are in references/commands.md.
Step 7 - Composition with sister tools
Before merge, apply adversarial review of new migrations - classify each as additive / breaking / data-loss / locking.
Worked example
Add an index on users.email and ship it through CI:
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Edit a previously-applied versioned migration | Checksum mismatch; validate fails on next run | Add a new V_n+1 migration that adjusts |
cleanDisabled=false in production config | One stray flyway clean drops the schema | Always cleanDisabled=true (Step 5) |
| Mixing versioned + repeatable migrations for the same object | Repeatable applies after every versioned change → race | Pick one per object class |
outOfOrder=true without team agreement | Lower-version migrations apply mid-stream; ordering breaks | Default false; enable per change with team review |
| Skip CI gating on per-PR ephemeral DB | Migrations break in production for the first time | Always run migrations in CI (Step 6) |
Limitations
References
Flyway command reference and CI integration
View source (opens in new window)Flyway command reference and CI integration
Command reference
Per fw-home (opens in new window), Flyway's commands are Migrate, Clean, Info, Validate, Undo, Baseline, Repair, Check, and Snapshot.
| Command | Use |
|---|---|
flyway migrate | Apply pending migrations |
flyway info | Show applied + pending migration list |
flyway validate | Verify checksums of applied migrations vs disk files |
flyway baseline | Mark a legacy schema state as baseline (skip prior migrations) |
flyway repair | Fix a broken flyway_schema_history (e.g., after a failed migration) |
flyway undo | Roll back the last versioned migration (Teams) |
flyway clean | Drop all objects in the schema (production-disabled by default) |
CI integration
Pattern: ephemeral DB (Docker / Testcontainers) per PR, apply migrations, run tests against the migrated schema.
- name: Spin up Postgres
uses: docker/setup-buildx-action@v3
- run: docker run -d --name pg -p 5432:5432 -e POSTGRES_PASSWORD=pwd postgres:16
- name: Apply migrations
run: |
docker run --rm --network=host \
-v "$PWD/db/migration:/flyway/sql" \
flyway/flyway -url=jdbc:postgresql://localhost:5432/postgres \
-user=postgres -password=pwd migrate
- name: Run tests
run: mvn testFor full integration with testcontainers (in the qa-test-environment plugin): spin up the DB via Testcontainers, then call Flyway.configure() in JUnit @BeforeAll.
Related skills
atlas-migrations
Authors and runs Atlas database schema migrations - declarative HCL or SQL schema definition with `atlas schema apply` for desired-state apply OR `atlas migrate diff` to generate versioned migrations against a dev DB; `atlas migrate apply` to deploy; `atlas migrate lint` to flag destructive / locking / data-loss patterns; `atlas migrate hash` to detect tampering. Supports PostgreSQL, MySQL, SQL Server, ClickHouse, SQLite, MariaDB, Snowflake, Oracle, Redshift, Spanner, CockroachDB, Databricks. Use when the user wants Terraform-style declarative DB schema management or modern SQL-first migration linting beyond Flyway / Liquibase.
liquibase-migrations
Authors and runs Liquibase database migrations - changelog-driven schema management with changesets in XML / YAML / JSON / SQL formats; supports `liquibase update` / `status` / `rollback` / `tag` / `history` lifecycle; offers per-changeset preconditions, contexts and labels for selective execution, and rollback semantics; tracks state in `DATABASECHANGELOG` + `DATABASECHANGELOGLOCK` tables. Use when the user works with Liquibase-managed schemas (Spring Boot heritage, polyglot DB shops), needs cross-DBMS portable migrations, or requires fine-grained rollback control.
migration-operation-taxonomy
Classifies every DDL and DML statement in a database migration into an eight-category operation taxonomy (additive, backwards-compatible alter, locking, lock-escalating, breaking, data-loss, unsafe default, index-missing foreign key) and assigns a Critical, Warning, or Info severity justified by the lock mode and table-rewrite behavior the target engine actually performs. Records where PostgreSQL and MySQL/InnoDB diverge for the same logical statement, and where behavior is version-gated (PostgreSQL 11 removed the table rewrite for a constant DEFAULT; MySQL 8.0.12 made ADD COLUMN instant). Use when a migration file appears in a diff or a review queue and someone must decide, before it reaches a production-sized table, which statements are safe and which will stall writes or destroy data.
sqlmesh-migrations
Authors and runs SQLMesh - data-transformation framework with version control, virtual data environments, automatic breaking-vs-non-breaking change classification, and downstream impact analysis; supports `sqlmesh init` / `plan` / `apply` / `run` / `audit` / `test` lifecycle; covers DuckDB, Postgres, Snowflake, BigQuery, Redshift, Databricks. Use when the user works with SQL data pipelines (warehouse + dbt-adjacent ELT), needs safer model evolution than dbt's deploy-and-pray, or wants the strongest impact-analysis story in the OSS data tooling space.