It’s common to want to introduce Flyway into a database that’s already running in production. For a new project, you can simply start writing migrations from V1, but things change when existing tables are already in place.
This article focuses specifically on baseline-on-migrate and walks through how to safely bolt Flyway onto an existing database that already contains data. For the basic concepts of Flyway and the setup procedure for a new project, see Database Migration Management with Flyway in Spring Boot — it’s worth reading first for a smoother start. For recovering from checksum mismatches, see the Flyway checksum mismatch resolution guide.
When you need baselineOnMigrate
A typical scenario is when tables like users or orders are already running in production, but the flyway_schema_history table doesn’t exist yet. Flyway has just been added as a dependency, and no migrations have been written yet.
If you enable Flyway in this state without any further setup, Flyway will conclude that “nothing has been applied yet” and try to run a script like V1__init.sql. But since the target tables already exist, the CREATE TABLE statement fails with a duplicate error, and the application fails to start.
This is a different situation from introducing Flyway into a brand-new project (applying V1 to an empty database). When bolting Flyway onto an existing database, you need an extra step: making Flyway recognize the current table structure as “some version.” That’s exactly the role of baseline-on-migrate.
Configuring baselineOnMigrate, baselineVersion, and baseline-description
Configure it in application.yml as follows:
spring:
flyway:
baseline-on-migrate: true
baseline-version: 1
baseline-description: "Existing schema (state before 2026-10 rollout)"
With baseline-on-migrate=true, the baseline process only runs when flyway_schema_history doesn’t exist. In environments where the history table already exists, nothing happens — so you can use the same configuration for both new and existing environments.
baseline-version specifies which version the existing schema should be treated as; the default is 1. The first migration you add from now on needs to have a number greater than this version. With baseline-version=1, you’d start from V2__add_email_column.sql.
baseline-description is optional, and defaults to << Flyway Baseline >> if omitted. Including a date or ticket number makes it easier for anyone reviewing the history later to understand the context. These properties have been supported out of the box since Spring Boot 2.x.
Gradle/Maven plugins require separate configuration
The spring.flyway.* properties in application.yml are only passed to the Flyway instance that Spring Boot auto-configures at startup. The Gradle plugin’s flywayInfo task, which you’d use for a dry-run check before applying changes to production, runs independently of Spring Boot’s context, so it doesn’t read spring.flyway.*.
This means you need to write the connection information and baseline settings separately in build.gradle as well.
flyway {
url = 'jdbc:mysql://localhost:3306/shop_db'
user = 'app_user'
password = System.getenv('DB_PASSWORD')
baselineOnMigrate = true
baselineVersion = '1'
baselineDescription = 'Existing schema (before 2026-10 rollout)'
}
For Maven, write the same items in the <configuration> section of the flyway-maven-plugin in pom.xml. If you forget this, flywayInfo won’t be able to find the connection target and will fail with an error.
Dry-run verification with flyway info before applying to production
Once the Gradle plugin is configured, check the state before applying anything.
./gradlew flywayInfo
Version | Description | Type | State
1 | Existing schema (before 2026-10 rollout) | BASELINE | Pending
2 | add email column | SQL | Pending
flyway info is read-only and writes nothing to the schema. If an unexpected script shows up as Pending, check its contents before running flyway migrate.
The first baseline marker row created in flyway_schema_history
When baseline-on-migrate runs, the following single row is added to flyway_schema_history.
SELECT version, description, type, success
FROM flyway_schema_history ORDER BY installed_rank;
version | description | type | success
1 | Existing schema (before 2026-10 rollout) | BASELINE | 1
2 | add email column | SQL | 1
The row with type set to BASELINE is the marker. No SQL is actually executed — only a record that “everything up to here is already applied” is stored. From this point on, Flyway only treats versions newer than this row as targets for migration: V1 is the baseline, and the first version that actually gets executed is V2.
Verifying in a staging environment
Always test this in staging before applying it to production. First, dump just the schema from production and restore it.
mysqldump -u app_user -p --no-data shop_db > schema.sql
mysql -u app_user -p shop_db_staging < schema.sql
Write the same values in the staging configuration file, start the application, and confirm that exactly one baseline row is created in flyway_schema_history. After that, add one migration and confirm that V2 and onward are applied correctly — once that’s verified, you can apply the same configuration to your production profile with confidence. If you’re unsure about the MySQL connection settings themselves, it’s also worth checking application.yml setup for connecting to MySQL in Spring Boot.
Common failures caused by a misconfigured baselineVersion
A common mistake is setting baseline-version equal to or greater than an actual migration version. If you set baseline-version=2 but also have a V2__add_email_column.sql, Flyway will consider V2 already baselined and skip it, so your intended change never gets applied.
Another common issue is applying Flyway to an existing database while leaving baseline-on-migrate=false (the default). Since the history table doesn’t exist, Flyway tries to run the first migration, which fails to start due to a duplicate error against the existing tables.
org.flywaydb.core.api.FlywayException: Found non-empty schema(s) "shop_db" but no schema history table.
If you see this error, check whether baseline-on-migrate is set to true, and whether baseline-version conflicts with your existing scripts.
Summary: the difference between a fresh install and a baseline install
Introducing Flyway into a new project is straightforward — you simply apply migrations starting from V1 to an empty database. Bolting it onto an existing database with live tables, on the other hand, requires an extra step via baseline-on-migrate: making Flyway recognize the current state as some version.
Set baseline-version and baseline-description, write the same connection info and baseline settings in your Gradle or Maven plugin configuration, check the Pending entries with flyway info, and test once in staging before rolling out to production. Doing so significantly reduces the risk of breaking your existing tables.
If you run into checksum mismatch errors after rollout, see the Flyway checksum mismatch resolution guide.