Database / Redgate Flyway Interview questions
Last updated
1. What is Flyway?
Flyway is a database migration tool that applies versioned changes to a database schema in a controlled, repeatable way. You keep each change as a small script (usually SQL), commit it to source control, and Flyway applies the scripts in order.
It records everything it has applied in a schema history table, so every environment (dev, test, production) can be brought to the same version by running one command. Flyway is now maintained by Redgate and ships as a command-line tool, Docker image, Maven and Gradle plugin, and Java API.
The idea is simple: treat the database like code, with version control and automated deployment.
Take quiz
A text file kept on the developer's laptop
The timestamp of the last Git commit
A cache inside the application server
A schema history table stored in the database
compress database backups
apply ordered, versioned schema changes to a database
tune slow SQL queries automatically
2. What is a Flyway migration?
A migration is a single script that changes the database, such as creating a table, adding a column, or loading reference data. Flyway treats each migration as one unit of work with a unique version (or a name, for repeatable ones).
Most migrations are plain SQL files, but Flyway also supports Java-based migrations when logic is too complex for SQL. Once a migration is applied, Flyway stores its version, description, checksum, and result in the history table.
A tiny example:
-- V1__create_customer.sql CREATE TABLE customer ( id INT PRIMARY KEY, name VARCHAR(100) NOT NULL );
Take quiz
a full copy of the production database
a runtime thread pool setting
one script that moves the schema from one state to the next
only the author's email address
the CPU usage of the server
version, description, checksum and success status
the full text of every SQL result set
3. What are the types of Flyway migrations?
Flyway supports three main kinds of migrations, each with a different filename prefix:
| Type | Prefix | Behaviour |
| Versioned | V |
Runs exactly once, in version order. |
| Repeatable | R |
Re-runs whenever its checksum changes. |
| Undo | U |
Reverses a versioned migration (paid editions). |
Newer editions also offer baseline migrations (prefix B) that collapse many old versions into one script for fresh databases. Callbacks are scripts too, but they hook into lifecycle events rather than being migrations.
Pick by the object you are changing: versioned for things that change state once (tables, columns, data), repeatable for definitions you want kept current in a single file.
Take quiz
Versioned migration
Repeatable migration
Baseline marker
Undo migration
V
R
X
M
4. How do you name a Flyway versioned migration?
Use the pattern V<version>__<description>.sql. It starts with the prefix V, then the version, then a double underscore, then a description with words separated by single underscores.
Versions can have several parts separated by dots or underscores, such as 1.2.3. Flyway compares them numerically part by part, so 1.10 is newer than 1.2.
V1__create_customer.sql V1_1__add_email_column.sql V2.0__create_orders.sql
The prefix, separator, and suffix can be changed through sqlMigrationPrefix, sqlMigrationSeparator, and sqlMigrationSuffixes, but the defaults are what most teams use.
Take quiz
V3_add_index.sql
3__add_index.sql
V3__add_index.sql
migration-v3-add-index.sql
Two underscores
A single hyphen
A colon
5. What is the flyway_schema_history table?
It is the table Flyway creates in your database to track every migration it has run. Flyway reads it before each migrate to decide what is pending, and updates it after each script.
Older releases called it schema_version. The name and schema can be changed with the table and defaultSchema settings. Key columns:
| Column | Meaning |
installed_rank |
Order in which scripts were applied |
version / description |
Parsed from the filename |
type / script |
SQL, JDBC, or baseline and the file name |
checksum |
CRC32 of the script content |
installed_by / installed_on |
Who ran it and when |
execution_time / success |
Duration and outcome |
Take quiz
checksum
installed_on
execution_time
flyway_log
db_changes
schema_version
migration_master
6. What are the core Flyway commands?
Flyway commands are run from the CLI, Maven, Gradle, or the API. The ones you will use daily are:
| Command | Purpose |
migrate |
Applies pending migrations |
info |
Shows applied, pending, and failed migrations |
validate |
Checks applied migrations against local files |
baseline |
Marks an existing database at a starting version |
repair |
Fixes the history table (failed rows, checksums) |
clean |
Drops all objects in the configured schemas |
Paid editions add undo, check, and other commands for reports, drift detection, and dry runs.
Take quiz
info
validate
baseline
migrate
info
repair
clean
migrate
7. What does the flyway migrate command do?
flyway migrate scans the configured locations, compares what it finds with the schema history table, and runs every pending migration in version order. Repeatable migrations whose checksum changed run last.
Before applying anything it validates previously applied scripts (validateOnMigrate is on by default) and creates the history table if it does not exist. If the database is non-empty and has no history table, migrate stops and asks you to baseline first.
flyway -url=jdbc:postgresql://localhost:5432/shop -user=app -password=secret -locations=filesystem:sql migrate
The command prints how many migrations were applied and the schema version it ended on, and it exits with code 0 only on success, so pipelines can rely on the exit status.
Take quiz
Ascending version number
Alphabetical by description
Newest file modification time first
Random order
Validation fails and migrate stops
Flyway silently re-runs it
Flyway deletes the history row
8. What is the purpose of the flyway info command?
flyway info prints a status table of every migration Flyway can see, both on disk and in the history table. It never changes the database schema, so it is safe to run at any time, including in production checks.
Typical states include Pending, Success, Failed, Missing, Out of Order, Ignored, and Future. Teams run it before a release to confirm what will be applied.
+-----------+---------+---------------------+------+---------+ | Category | Version | Description | Type | State | +-----------+---------+---------------------+------+---------+ | Versioned | 1 | create customer | SQL | Success | | Versioned | 2 | add email column | SQL | Pending | +-----------+---------+---------------------+------+---------+
Take quiz
Pending
Missing
Future
Yes, it applies pending scripts
Yes, it drops unused tables
No, it only reads and reports
Yes, it rewrites checksums
9. What is the purpose of flyway validate?
flyway validate checks that the migrations already applied to the database still match the scripts you have locally. It compares versions, descriptions, and checksums, and fails if anything differs.
It catches problems early, for example someone editing an old script, deleting one, or merging a branch that introduced a lower version. Run it in pull-request builds so bad changes never reach a shared environment.
It only reads the database, so it is cheap and safe to run on every build, long before anything is deployed.
Take quiz
File sizes versus disk quota
Author names versus Git users
Table row counts versus file line counts
Stored checksums versus current file checksums
Only after dropping the database
Inside application business logic
On the developer's printer
In CI builds before merging or deploying
10. What is the purpose of flyway baseline?
baseline tells Flyway that an existing database is already at a certain version, so older migrations should not run. It creates the history table and inserts a single baseline row.
You use it when adopting Flyway on a database that was built by hand or by another tool. Set the version with baselineVersion, and every migration at or below that version is skipped. Only higher versions run on the next migrate.
flyway -baselineVersion=5 -baselineDescription="Existing prod schema" baseline
Baselining is a one-time step per database. Afterwards the history table shows a baseline row, and Flyway behaves as it would on a database it built itself.
Take quiz
All versions from 1
Only version 5
Nothing, ever
Only versions above 5
creating a brand-new empty database
exporting data to CSV
adopting Flyway on an already populated database
11. How do you configure Flyway?
Flyway reads settings from several places, and later sources override earlier ones: config files, then environment variables, then command-line arguments. In Flyway 10 and later, a flyway.toml file with named environments is the recommended style, and the classic flyway.conf still works.
# flyway.conf flyway.url=jdbc:postgresql://localhost:5432/shop flyway.user=app flyway.locations=filesystem:sql flyway.baselineOnMigrate=false
Environment variables use the form FLYWAY_URL, FLYWAY_USER, and so on. This is handy for CI, where secrets should not be stored in files.
If a value seems to be ignored, run with -X. The debug output prints the resolved configuration, so you can see which source won.
Take quiz
The default config file
Command-line arguments
A comment in a SQL file
flyway-url
FLYWAY_URL
URL_FLYWAY
FLYWAY.DB
12. What are Flyway placeholders?
Placeholders are named variables written as ${name} inside SQL migrations. Flyway replaces them with configured values before running the script, which lets one file work across environments.
Define them with flyway.placeholders.name=value or -placeholders.name=value. Flyway also provides built-ins such as ${flyway:defaultSchema}, ${flyway:user}, and ${flyway:timestamp}.
CREATE TABLE ${schema_name}.audit_log ( id INT PRIMARY KEY, note VARCHAR(200) );
If a placeholder is used but never defined, the migration fails rather than running with a literal ${...}.
Placeholders also work in callbacks. If ${ } clashes with your SQL, change the delimiters with placeholderPrefix and placeholderSuffix.
Take quiz
${name}
#{name}
%name%
{{name}}
Flyway uses an empty string silently
Flyway picks a random value
The migration fails with an error
The line is skipped without notice
13. What are repeatable migrations used for?
Repeatable migrations manage objects that are better redefined than altered, such as views, stored procedures, functions, and triggers. They are named R__description.sql and have no version.
Flyway re-applies one whenever its checksum changes, and always after all pending versioned migrations. They run in alphabetical order of description, so write them with CREATE OR REPLACE so they can safely run again.
-- R__customer_summary_view.sql CREATE OR REPLACE VIEW customer_summary AS SELECT id, name FROM customer;
Because repeatables run in alphabetical order, name them so dependencies come first, for example R__01_base_view.sql before R__02_report_view.sql.
Take quiz
Primary key values
Views and stored procedures
Application log files
Backup schedules
On every single migrate regardless of content
Only on the first day of the month
When its checksum changes
14. What are Flyway callbacks?
Callbacks are scripts or Java classes that Flyway runs automatically at specific points in its lifecycle. They are useful for housekeeping around a deployment, such as refreshing permissions or logging.
SQL callbacks are placed in the same location as migrations and named after the event, for example beforeMigrate.sql or afterMigrate.sql. Common events:
| Event | Runs |
beforeMigrate / afterMigrate |
Once around the whole migrate run |
beforeEachMigrate / afterEachMigrate |
Around every single migration |
beforeValidate, beforeClean |
Around validate and clean commands |
A callback can also be a Java class implementing the Callback interface, which suits things like sending a notification after a deployment.
Take quiz
afterMigrate.sql
afterEach.sql
V0__after.sql
replacing the schema history table
choosing the JDBC driver
hooking tasks into Flyway lifecycle events
encrypting migration files
15. What is the purpose of flyway clean?
flyway clean drops every object in the configured schemas: tables, views, procedures, and the history table itself. It gives you a blank slate, which is handy before re-running all migrations in a throwaway dev or test database.
It is destructive and must never be pointed at production. Recent Flyway versions disable it by default; you must explicitly set cleanDisabled=false to use it.
flyway -cleanDisabled=false clean migrate
A common local workflow is clean followed by migrate. It rebuilds the database from scratch and proves your scripts alone can recreate the schema.
Take quiz
Only rows older than 30 days
All objects in the configured schemas
Only the history table
Only the latest migration
Production during business hours
Any shared database to save space
Read replicas used by analysts
Disposable dev or test databases
16. What are the editions of Flyway?
Flyway comes in a free Community edition and paid Teams and Enterprise editions from Redgate. Community is open source and covers the core flow: versioned and repeatable migrations, callbacks, placeholders, and the main commands.
The paid editions add features aimed at larger teams, such as undo migrations, dry runs, change and drift reports, additional database support, and vendor support. Exact feature lists and database coverage change between releases, so check the current Redgate documentation before choosing.
Community is enough for many projects, so start there and move up only when you hit a specific missing feature.
Take quiz
Enterprise
Community
Teams
Ultimate
only the Community edition
no edition at all
the paid editions
17. How do you use Flyway with Spring Boot?
Add flyway-core (plus the database module, such as flyway-database-postgresql, for newer versions) to the build. Spring Boot auto-configures Flyway, and it runs migrate on startup before the JPA layer touches the database.
By default it reads scripts from classpath:db/migration, so place files in src/main/resources/db/migration. Tune it with spring.flyway.* properties.
spring.flyway.enabled=true spring.flyway.locations=classpath:db/migration spring.flyway.baseline-on-migrate=false spring.jpa.hibernate.ddl-auto=validate
Spring Boot waits for migrate to finish before building the JPA layer, so entities never see a half-updated schema. Set baseline-on-migrate only when adopting Flyway on an existing database.
Take quiz
src/test/flyway
classpath:db/migration
classpath:sql/scripts
ddl-auto=create-drop
ddl-auto=update on production
ddl-auto=validate
ddl-auto=random
18. How do you use the Flyway Java API?
Build a Flyway instance with the fluent configuration API, then call a command on it. This is useful when an application should migrate its own database at startup without Spring Boot.
Flyway flyway = Flyway.configure() .dataSource("jdbc:postgresql://localhost:5432/shop", "app", "secret") .locations("classpath:db/migration") .baselineOnMigrate(false) .load(); MigrateResult result = flyway.migrate(); System.out.println("Applied: " + result.migrationsExecuted);
Other commands such as info(), validate(), and repair() are available on the same object.
Calling migrate() again is safe, since with nothing pending it does nothing. Let the FlywayException propagate at startup so the application never runs against an outdated schema.
Take quiz
start()
compile()
build-all()
load()
flyway.apply-all()
flyway.migrate()
flyway.deploy-now()
flyway.commit()
19. What is the difference between versioned and repeatable migrations?
A versioned migration has a unique version and runs once. A repeatable migration has no version and runs again every time its checksum changes. This single difference drives how you use them.
| Aspect | Versioned (V) | Repeatable (R) |
| Filename | V2__add_table.sql |
R__orders_view.sql |
| Runs | Exactly once | Whenever content changes |
| Order | By version number | After versioned, by description |
| Editing after apply | Not allowed (checksum error) | Expected and safe |
| Best for | Tables, columns, indexes, data fixes | Views, procedures, functions |
Use CREATE OR REPLACE inside repeatable scripts so a rerun does not fail.
A practical rule: if rerunning the script is harmless and the latest definition should always win, make it repeatable. Otherwise make it versioned.
Take quiz
Versioned
Baseline row
Both are blocked equally
Repeatable
Repeatable migrations
Versioned migrations
Undo migrations
20. How does Flyway order migrations?
Flyway sorts versioned migrations by version, comparing each dot or underscore separated part as a number. So 1.2 comes before 1.10, and 2 comes after both. Letters in file names do not affect order, only the version does.
After all pending versioned migrations, Flyway runs repeatable migrations, sorted alphabetically by description. Two migrations with the same version cause an error, because the version is the unique key.
V1__init.sql -> 1st V1.2__index.sql -> 2nd V1.10__audit.sql -> 3rd R__reports.sql -> last
You can see the resolved order at any time with flyway info, which lists versions in the sequence Flyway will use.
Take quiz
1.2 is newer than 1.10
1.10 is newer than 1.2
They are equal
The newer file silently wins
Flyway reports an error
They run in parallel
Flyway renames one automatically
21. How does Flyway detect modified migrations?
When a migration runs, Flyway stores a CRC32 checksum of the script content in flyway_schema_history. On later runs it recalculates the checksum from the file and compares it.
If the numbers differ for a versioned migration, validation fails with a message like Migration checksum mismatch. Line endings and whitespace changes also alter the checksum, which is why mixed Windows and Linux line endings sometimes cause surprise failures. Repeatable migrations are the exception, because a changed checksum simply triggers a re-run.
The error message prints both the applied and the resolved checksum, so you can tell which side changed. Run flyway validate to see it without applying anything.
Take quiz
A full backup of the file
A CRC32 checksum
The Git author name
The file's last-opened time
Flyway compares screenshots
Whitespace and line endings change the checksum
Whitespace affects table names
Flyway ignores content and checks owners
22. What is the purpose of flyway repair?
repair fixes problems in the schema history table so you can continue. It does not change your application tables.
It does two main things:
- Removes failed migration entries (on databases without transactional DDL, where a half-applied script leaves a failed row).
- Realigns stored checksums, descriptions, and types with the files currently on disk.
Only run it after you have understood the cause. If the failed script partly changed the schema, undo those changes manually first, otherwise the next migrate will fail again.
flyway repair
If a checksum change was intentional and harmless, such as a fixed comment, repair is the supported way to update the stored value. Note the reason in your commit message.
Take quiz
Yes, it recreates all tables
No, only the history table
Yes, it deletes old rows
Yes, it reorders columns
clean up any partial changes the failed script left
delete the Flyway jar
rename every migration
23. When should you use baselineOnMigrate?
Use baselineOnMigrate=true when you run migrate against a non-empty schema that has no history table, and you want Flyway to baseline it automatically instead of failing.
It is convenient for rolling Flyway out across many existing databases in a pipeline. The risk is that it will baseline any non-empty schema, including one you pointed at by mistake, and then skip migrations at or below baselineVersion. For a one-time adoption, an explicit baseline command is safer, and the flag should be turned off afterwards.
flyway -baselineOnMigrate=true -baselineVersion=3 migrate
With the flag on, the first migrate inserts a baseline row at baselineVersion (default 1), then applies anything higher. Always confirm that version first.
Take quiz
an empty schema with a history table
a failed network connection
a non-empty schema without a history table
It slows down SQL parsing
It may baseline the wrong non-empty schema by accident
It removes checksums
It disables callbacks
24. What happens when a Flyway migration fails?
The outcome depends on whether the database supports transactional DDL. Each migration runs in its own transaction by default.
On PostgreSQL or SQL Server, a failure rolls the whole script back, nothing is recorded, and you fix the file and rerun. On MySQL or Oracle, DDL statements commit implicitly, so earlier statements in the script may already be applied. Flyway then records the migration as failed and refuses to run further migrations.
To recover: manually revert the partial changes, run flyway repair to remove the failed entry, correct the script, and run migrate again.
Either way the command exits non-zero, so a pipeline stops at that stage. Read the error for the script name and line before touching the database.
Take quiz
DDL statements commit implicitly there
Flyway ignores errors on MySQL
MySQL has no tables
Flyway always disables transactions
Migrate again, then repair
Delete all data, then baseline
Revert partial changes, repair, fix script, migrate
Run clean on production
25. Why should applied migrations never be edited?
Because the migration is the historical record of how a database reached its current shape. If you edit it after it ran, databases that already applied the old version no longer match the file, and any new database would get a different result.
Flyway protects against this with checksums, so validation fails on the edited file. The correct approach is to add a new migration that makes the fix, for example an ALTER TABLE after a wrong column type. Editing is only acceptable for a script that has never left your own local database.
Enforce this in code review and CI, because validate will fail on every environment that already ran the original.
Take quiz
add a new migration that corrects it
edit the old file and hope
delete the history table in production
rename the file to a lower version
upgrade faster
drift apart and fail checksum validation
share one encrypted copy
26. How does Flyway handle transactions?
By default Flyway wraps each migration in its own transaction. If any statement fails, the transaction rolls back, as long as the database supports transactional DDL.
You can change the behaviour:
group=trueruns all pending migrations in a single transaction.mixed=trueallows transactional and non-transactional statements in one run.executeInTransaction=falsein a script config file disables the transaction for that script.
Some statements, such as CREATE INDEX CONCURRENTLY in PostgreSQL, cannot run inside a transaction and need the non-transactional option.
Check how your database treats DDL before relying on rollback. Keep risky scripts small, so a failure leaves less to clean up, and test them on a copy first.
Take quiz
One transaction per migration
One transaction per database server
No transactions at all
SELECT 1
CREATE INDEX CONCURRENTLY in PostgreSQL
CREATE TABLE in a test schema
INSERT of a single row
27. When should you enable outOfOrder in Flyway?
outOfOrder=true lets Flyway apply a pending migration whose version is lower than the latest applied version. Normally such a script is ignored and validation flags it.
A common case is two branches: branch A merges V5 and deploys, then branch B merges V4. Without the flag, V4 is never applied. Enable it only when you accept that order of execution can differ between environments, and make sure scripts are independent of each other. Many teams avoid the problem by using timestamp versions plus careful merging.
flyway -outOfOrder=true migrate
After the late script runs, info shows it with the state Out of Order, so you can still see it ran later than its version suggests.
Take quiz
Skipping the history table
Applying a lower version after a higher one is already applied
Running two databases at once
Editing checksums freely
It deletes older tables
It disables validation forever
It only works with Java
Execution order may differ between environments
28. How do you write a Flyway Java-based migration?
Create a class that extends BaseJavaMigration and put it in a package on a configured location. The class name follows the same naming rule as SQL files, with V, version, double underscore, then description.
package db.migration; import org.flywaydb.core.api.migration.BaseJavaMigration; import org.flywaydb.core.api.migration.Context; import java.sql.Statement; public class V3__Normalize_emails extends BaseJavaMigration { @Override public void migrate(Context context) throws Exception { try (Statement st = context.getConnection().createStatement()) { st.execute("UPDATE customer SET email = LOWER(email)"); } } }
Use Java when you need loops, conditionals, or calls to libraries, for example transforming rows in code.
Java migrations are compiled and must be on the classpath. They appear in the history table with type JDBC, and their checksum is only recorded if you override getChecksum().
Take quiz
AbstractFlywayScript
SqlMigrationBase
BaseJavaMigration
JdbcRunner
migrate(Context context)
run()
execute-all()
29. What is the difference between Flyway and Liquibase?
Both are migration-based tools that track applied changes in a table, but they differ in how changes are written and in philosophy.
| Aspect | Flyway | Liquibase |
| Change format | Plain SQL (or Java) files | XML, YAML, JSON, or SQL changelogs |
| Ordering | File version in the name | Order of changesets in the changelog |
| Tracking table | flyway_schema_history |
DATABASECHANGELOG |
| Learning curve | Very low, just SQL | Higher, own changelog syntax |
| Database portability | You write dialect-specific SQL | Abstract change types can generate per-database SQL |
Pick Flyway when your team is comfortable with SQL and wants simplicity. Pick Liquibase when you want database-neutral change definitions or richer conditional logic.
Take quiz
Liquibase
Flyway
Both identically
plain SQL files
XML changelogs only
binary patches
Excel sheets
30. How do you manage Flyway configuration across environments?
Keep the migration scripts identical everywhere and vary only the connection and environment settings. Three common techniques:
- Separate config files, for example
flyway-dev.confandflyway-prod.conf, passed with-configFiles. - Environment variables such as
FLYWAY_URLinjected by the CI system. - Named environments in
flyway.toml, selected with-environment=prod.
Use placeholders for values that differ inside SQL, like schema names or grantee roles. Never keep passwords in files committed to Git.
A tidy layout is one shared sql folder plus a small config per environment. Use -configFiles to layer a base file with an environment override, so only changed keys are repeated.
Take quiz
The SQL migration history itself
Connection details and placeholder values
The Flyway checksum algorithm
The version numbering scheme
-environment=prod
-profile-prod
-target=env
-site=prod.conf
31. How do you use Flyway with Docker?
Redgate publishes the flyway/flyway image. You mount your SQL folder and pass configuration as arguments or environment variables, which gives you a tool-free runner in CI.
docker run --rm \ -v "$PWD/sql:/flyway/sql" \ flyway/flyway \ -url=jdbc:postgresql://db:5432/shop \ -user=app -password="$DB_PASSWORD" \ migrate
In Docker Compose, add a Flyway service with depends_on the database, so migrations run once the database is healthy and before the application starts.
Pin the image tag, for example flyway/flyway:10, so builds stay reproducible. Extra JDBC drivers can be mounted into /flyway/drivers, and CI should check the container exit code to catch failed migrations. Use a named network so the container can reach the database.
Take quiz
/flyway/sql
/usr/share/db
/var/log/flyway
/home/app/migrations
before the network exists
after the database is ready
only after production deploy
32. How does Flyway prevent concurrent migrations?
When migrate starts, Flyway takes a lock so that only one process applies changes at a time. The mechanism depends on the database: it can be an advisory lock, a table lock on the schema history table, or a lock row.
A second instance, for example another application pod starting at the same time, waits for the lock, then finds that nothing is pending and exits cleanly. This is why running migrate from several replicas is generally safe, though many teams still prefer a single dedicated migration job to keep startup predictable.
In Kubernetes, an init container or a Helm pre-upgrade job is a common home for this single migration run.
Take quiz
It runs the same scripts again at once
It drops the history table
It waits, then sees nothing pending
speed up SQL parsing
encrypt the migrations
stop two processes applying changes simultaneously
limit file sizes
33. Why is flyway clean disabled by default?
Because clean drops every object in the configured schemas and cannot be undone. A single wrong URL in a pipeline could wipe a production database.
Flyway therefore sets cleanDisabled=true by default in recent releases. Teams enable it only in local or ephemeral test environments, usually through a developer-only config file. In production configs, leave it disabled explicitly so a mistaken override is harder to make.
To use it deliberately, pass -cleanDisabled=false for one run or keep it in a local-only config. Some teams add a pipeline guard that rejects the flag when the URL points at production. The default is deliberately the safe one.
Take quiz
It permanently removes all objects in the schemas
It is slower than migrate
It requires Java 8
It needs a paid license
Local or ephemeral test databases
Production
Shared staging with real customer data
Disaster recovery sites
34. How do you roll back a Flyway migration?
Flyway Community is a roll-forward tool: you fix problems with a new migration rather than reversing an old one. For many teams this is the safest approach, since data changes are rarely cleanly reversible.
The options are:
- Undo migrations (
U2__...sql) withflyway undo, available in paid editions. - Restore a backup or snapshot taken before the deployment.
- Roll forward by writing a new versioned script that reverses the change.
Whichever you choose, test the rollback path on a copy of production data before release day.
Destructive changes, such as dropping a column, lose data that an undo script cannot bring back, so take a backup before the release.
Take quiz
Editing the old script
Deleting the history table
Rolling forward with a new migration
Running clean on production
flyway revert-all
flyway undo
flyway back
35. How do you integrate Flyway into a CI/CD pipeline?
Treat migrations like application code: they are reviewed in a pull request, tested automatically, and deployed by the pipeline using the same scripts that ran in earlier stages.
- Pull request: run
flyway validateand amigrateagainst a throwaway database to prove the scripts apply cleanly. - Build: package the SQL folder as an artifact, or bake it into a Docker image.
- Deploy: run
flyway infoto preview, thenflyway migrateper environment before the application rollout. - Verify: fail the stage if the exit code is non-zero, and publish the info output as a build record.
# GitHub Actions step - name: Migrate database run: | docker run --rm -v "$PWD/sql:/flyway/sql" flyway/flyway \ -url="$DB_URL" -user="$DB_USER" -password="$DB_PASSWORD" migrate
Store credentials in the CI secret store and pass them as environment variables.
Take quiz
flyway clean
flyway baseline on prod
flyway validate
Inside a SQL migration
In the Git repository's README
In a public config file
In the CI secret store
36. How do you migrate to a specific version using target?
Set target to a version and Flyway applies pending migrations only up to and including it. Anything higher stays pending.
This is useful to reproduce a bug at an older schema, stage a release in steps, or test an upgrade path. Special values are latest (default), current, and next.
flyway -target=2.5 migrate
Remember that the target also affects validation: migrations above the target are not treated as errors, they are simply not yet applied.
A typical use is a staged release: migrate to the last version the old application understands, deploy the app, then run the rest. Running info afterwards confirms what is still pending.
Take quiz
Deletes versions above 2.5
Renames files to 2.5
Applies pending migrations up to version 2.5
Locks the database at 2.5 forever
next-only
zero
latest
all-or-none
37. Explain the execution flow of flyway migrate?
When you run migrate, Flyway follows a fixed sequence. Understanding it helps when you debug unexpected behaviour.
flowchart TD
A["Start migrate"] --> B["Connect and acquire lock"]
B --> C{History table exists?}
C -- No, empty schema --> D["Create history table"]
C -- No, schema not empty --> E["Fail or baseline if baselineOnMigrate"]
C -- Yes --> F["Validate applied migrations"]
D --> G["Scan locations for scripts"]
F --> G
G --> H["Pick pending versioned migrations"]
H --> I["Run each in a transaction and record result"]
I --> J["Run changed repeatable migrations"]
J --> K["Run afterMigrate callbacks and release lock"]
Callbacks such as beforeMigrate fire at the start, and beforeEachMigrate around every script. If a script fails, Flyway stops there, releases the lock, and returns a non-zero exit code. Scripts already committed stay applied.
Validation happens before any new script runs, which is why one edited old file blocks the whole deployment. That is intentional: Flyway stops rather than guessing.
Take quiz
Creates the history table
Runs clean
Aborts with a licence error
Deletes the SQL folder
before the lock is taken
instead of versioned ones
after pending versioned migrations
38. How do you achieve zero-downtime migrations with Flyway?
Flyway only runs the scripts. Zero downtime comes from how you design them: every change must be compatible with both the old and new application versions during rollout. The standard pattern is expand and contract.
- Expand: add the new column or table as nullable, with no destructive change. Deploy migration first.
- Migrate data: backfill in small batches, keep old and new columns in sync.
- Switch: deploy the new application version that uses the new structure.
- Contract: in a later release, drop the old column with a new versioned migration.
Avoid long locks: create indexes concurrently where supported, add columns without volatile defaults on large tables, and never rename a column in one step while the old code is still running. Also run migrations before the application rollout, not inside every pod, so a slow script does not block startup.
Take quiz
Drop the old column
Add new structures without removing old ones
Restart the database
renaming in place during peak traffic
running clean first
adding a new column, syncing, then dropping the old one later
disabling checksums
39. How do you introduce Flyway to an existing production database?
The goal is to put the current schema under version control without re-running history. A careful approach looks like this:
- Extract the current schema as a script, for example
V1__baseline.sql, using your database's dump tool, and use it to build new dev databases. - In production, run
flyway baseline -baselineVersion=1so Flyway records that version 1 already exists there. - Add all further changes as
V2and above. - Run
flyway infoandvalidateagainst a restored copy of production first.
flyway -baselineVersion=1 -baselineDescription="Initial prod state" baseline
Verify the extracted script really recreates production's schema, otherwise new environments will drift from it. Paid editions can generate a baseline migration to help here.
Record the cutover in the runbook, including who ran baseline and when, so later audits can trace the starting point.
Take quiz
Version 1 is already in place, skip it
Delete everything older than 1
Run V1 again
Lock all tables
a developer's local notes
an empty unrelated database only
the live primary during peak hours
a restored copy of production
40. How do you troubleshoot a Flyway validate failed error?
Read the message first, because Flyway names the exact script and the cause. The common ones:
| Message theme | Likely cause | Fix |
| Checksum mismatch | An applied script was edited | Revert the file, or if intentional and safe, run repair |
| Detected resolved migration not applied | Lower version merged after higher one deployed | Use outOfOrder or renumber |
| Detected applied migration not resolved locally | Script deleted or wrong locations |
Restore the file or fix the path |
| Detected failed migration | A previous run failed | Fix partial changes, repair, rerun |
Run flyway info to see states, and add -X for debug logs to confirm which locations Flyway actually scanned. Use ignoreMigrationPatterns only for known, understood cases. Check line endings too, since Git autocrlf is a classic source of surprise checksum errors.
Take quiz
an already applied script was modified
the database is offline
the JDBC driver is old
the history table has no rows
-quiet-only
-X
-debugfile=none
41. How do you handle large data migrations in Flyway?
Separate schema changes from bulk data work. A single UPDATE across hundreds of millions of rows can hold locks, bloat the transaction log, and make the deployment time unpredictable.
Better options:
- Break the work into batches inside a Java migration, committing between batches (with a non-transactional setup if needed).
- Run the heavy backfill as a separate job outside the deployment, and keep only the quick schema change in Flyway.
- Add the new column first, backfill gradually, then add constraints in a later migration.
- Test with production-sized data to measure runtime.
Avoid putting long-running scripts in the same release window as other changes. If a script must run non-transactionally, make it idempotent so it can be safely retried after a failure.
Take quiz
Long locks and unpredictable deploy time
SQL forbids updates
Flyway cannot read large files
encrypted
version-less
idempotent
auto-generated
42. How can you use Flyway in a multi-tenant architecture?
The right approach depends on the tenancy model. In both cases the same scripts must reach every tenant.
| Model | Approach |
| Database per tenant | Loop over tenant connection details and run migrate for each, ideally in parallel with a concurrency cap |
| Schema per tenant | Run migrate with schemas=tenant_a, then the next tenant, or use the Java API to iterate |
| Shared schema | Run once; tenant id is a column, so no per-tenant migration is needed |
for tenant in $(cat tenants.txt); do flyway -url="jdbc:postgresql://db/${tenant}" -user=app migrate || exit 1 done
Track which tenants succeeded, since a failure part-way leaves tenants on different versions. Tenant-specific data should come from placeholders or callbacks, not separate migration files.
Parallel runs shorten the total time, but cap the concurrency so the database server is not overloaded.
Take quiz
Run it once for all tenants automatically
Edit the history table by hand
Use undo for each tenant
Run migrate once per tenant schema
Flyway deleting the loop
tenants ending up on different versions after a mid-loop failure
checksums becoming negative
the SQL folder being locked
43. How do you avoid version conflicts in team development?
Conflicts happen when two developers pick the same next number, for example both create V8. Flyway then fails with a duplicate version error.
Practical ways to avoid this:
- Use timestamp versions such as
V202610141530__add_index.sql, which almost never collide. - Keep one directory of migrations and let CI run
validateon every pull request, so clashes surface before merge. - Enable
outOfOrderin dev only, if branches routinely merge in a different order than they deploy. - Never renumber a file after it was merged and applied anywhere.
Agree on one convention for the team and write it in the contributing guide.
When a clash does slip through, the later branch should rename its file to a new version before merging. That is safe because the script has not been applied anywhere yet.
Take quiz
They remove checksums
They run faster
Two developers rarely pick the same value
They avoid the history table
In production at midnight
CI validation on pull requests
In a post-release survey
44. What is a custom MigrationResolver and when would you use it?
A MigrationResolver is a Flyway extension point that tells Flyway where migrations come from. The built-in resolvers find SQL files and Java classes; a custom one lets you supply migrations from somewhere else.
Examples include generating migrations from a template, loading them from a remote store, or building them from annotations or metadata. You implement the interface, return a list of ResolvedMigration objects, and register it with resolvers. Setting skipDefaultResolvers=true turns off the standard ones.
public class TemplateResolver implements MigrationResolver { @Override public Collection<ResolvedMigration> resolveMigrations(Context context) { // build and return ResolvedMigration objects return List.of(); } }
Most teams never need this; reach for it only when files and classes genuinely do not fit.
Resolved migrations still go through checksum validation, so generated content must be deterministic. If the output changes between runs, validate will fail.
Take quiz
A new database driver
A faster checksum
A source of migrations beyond files and classes
skipDefaultResolvers
noBuiltinScan
resolverOff
disableLookup
45. How do you secure database credentials in Flyway?
Keep credentials out of migrations and out of Git. The URL, user, and password belong to the runtime environment, not the repository.
- Pass them as environment variables (
FLYWAY_PASSWORD) injected from your CI secret store. - Use a secret manager such as Vault, AWS Secrets Manager, or Azure Key Vault to fetch values at runtime, and note that paid editions also include resolvers for some of these.
- Prefer short-lived or IAM-based credentials where the database supports them.
- Give the migration account only the rights needed for DDL, and use a different, less privileged account for the application.
Remember that command-line arguments can be visible in process listings, so environment variables or secret files are safer than -password= typed inline.
Take quiz
An injected environment variable from a secret store
A comment in V1__init.sql
A committed flyway.conf
A shared wiki page
Least privilege for the application
It speeds up queries
Flyway requires two accounts
It avoids checksums
46. How do you test Flyway migrations?
Test that scripts apply cleanly, and that the resulting schema and data behave as expected, before they reach shared environments.
- Fresh build: start an empty database (Docker or Testcontainers), run
migrate, and confirm it succeeds. - Upgrade path: restore a production-like snapshot at the previous version and migrate to the new one.
- Assertions: run integration tests or SQL checks for expected tables, constraints, and data.
- Validate: run
validateto catch edited or missing scripts.
@Container static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16"); @Test void migrationsApply() { Flyway f = Flyway.configure() .dataSource(db.getJdbcUrl(), db.getUsername(), db.getPassword()).load(); assertTrue(f.migrate().success); }
Use the same database engine and major version as production, because SQL dialects differ.
Keep test-only seed data in a separate location, selected with a different locations value, so it never reaches production.
Take quiz
SQL dialects and DDL behaviour differ between databases
Flyway only supports one
It makes files shorter
Checksums depend on the engine name
Migrating a production-like snapshot from the previous version
Running only on an empty database
Running only a syntax linter
47. How do you use ignoreMigrationPatterns safely?
ignoreMigrationPatterns tells validation to tolerate certain migration states instead of failing. It replaces the older ignoreMissingMigrations and ignoreFutureMigrations flags. A pattern has the form type:state, for example:
| Pattern | Meaning |
*:missing |
Ignore applied migrations whose file is not present locally |
*:future |
Ignore applied migrations newer than anything local |
repeatable:missing |
Ignore only missing repeatable migrations |
flyway -ignoreMigrationPatterns="*:future" validate
Use it sparingly, for known cases such as deploying an older branch to a database that a newer branch already upgraded. Broad patterns hide real drift, so scope them to a type and state, and remove them once the situation passes.
Review the setting at every upgrade. A pattern that lingers after its cause is gone can silently hide a real problem, so remove it as soon as the situation passes.
Take quiz
Pending scripts
Applied migrations newer than any local script
Callbacks
They run slower
Broad patterns can hide real schema drift
They break Java
They delete rows
48. What does Flyway Desktop add to the command-line workflow?
Flyway Desktop is Redgate's graphical tool that sits on top of the same migration engine. It does not replace the CLI; the migrations it produces are standard Flyway scripts you run in your pipeline.
Its main value is helping developers generate migration scripts from schema changes. You edit a development database, compare it with a schema model kept in source control, and Desktop produces the versioned script for you. It also helps with schema comparison, drift review, and shadow databases for building and checking scripts.
Teams often use Desktop locally to author changes and the CLI or Docker image in CI to deploy them. Some of these features are licensed, so check the edition you have.
Feature availability depends on your licence and version, so confirm it in the Redgate documentation.
Take quiz
Only by clicking the app on production
As binary patches
Through email attachments
As standard Flyway scripts via the CLI or Docker
Replacing the history table
Generating migration scripts from schema differences
Hosting your database
Compressing backups
49. Which is better and why: SQL or Java migrations in Flyway?
SQL is the default and the better choice for most changes. Java is a specialist tool for logic that SQL cannot express well.
| Aspect | SQL migration | Java migration |
| Readability | Anyone with SQL can review it | Needs Java knowledge |
| Tooling | Easy to run in a DB client | Needs build and compile step |
| Logic | Limited to what the dialect allows | Loops, libraries, conditions |
| Portability | Dialect-specific | Can adapt through JDBC code |
| Typical use | DDL, simple data changes | Complex data transforms, calling services |
Start with SQL. Move to Java when you need row-by-row transformation, batching with commits, or reuse of application code. Keep Java migrations free of dependencies on changing application classes, because they must keep working years later.
Whichever you pick, write the rule in the team guidelines so every new migration follows it.
Take quiz
Creating a simple table
Complex row-by-row data transformation
Adding one index
Renaming a file
Flyway blocks them
They skip checksums
The classes may change, breaking old migrations
50. Explain the lifecycle of a Flyway migration from commit to production?
A migration moves through the same stages as any other code change, with extra checks because it alters shared state.
sequenceDiagram
participant Dev as Developer
participant Git as Git / PR
participant CI as CI pipeline
participant Test as Test DB
participant Prod as Production DB
Dev->>Git: Commit V12__add_index.sql
Git->>CI: Trigger build
CI->>Test: flyway validate and migrate on fresh DB
Test-->>CI: Success
CI->>Prod: flyway info (preview pending)
CI->>Prod: flyway migrate
Prod-->>CI: Row added to flyway_schema_history
- Author: write a new versioned script, never editing old ones.
- Review: a teammate checks SQL for locks, data impact, and naming.
- Verify: CI builds a fresh database and upgrades a snapshot.
- Deploy: the pipeline runs
migratewith the checksums recorded in the history table. - Observe: confirm with
info, and keep the previous version available for roll-forward or restore.
If production fails, the failed script is visible in the history table and the logs, which is the first place to look.