# 6. Baselining an existing database [← 5. Repeatable migrations](05-repeatable-migrations.md) · [Index](../README.md) · Next: [7. Why there is no undo →](07-why-there-is-no-undo.md) Every migration tool eventually meets a database it didn't create. Someone hand-ran DDL for five years, and now the team wants Flyway to take over from here — without dropping the schema and replaying history that never actually happened through Flyway. [`FlywayBaselineTest`](../src/test/java/com/ankurm/dbmigrations/flyway/FlywayBaselineTest.java) sets this up literally: a `legacy_account` table is created with plain JDBC, with one row already in it, *before Flyway is ever involved*. Pointing an unconfigured Flyway at that database refuses outright — this is the same safety check from chapter [1](01-the-problem-and-mental-model.md), and it's what makes baselining necessary rather than optional: ``` FlywayException: Found non-empty schema(s) "PUBLIC" but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table. ``` Turning on `spring.flyway.baseline-on-migrate=true` (with `baseline-version=1` and a `baseline-description`) fixes it. `V1__init.sql` — written to describe the schema that *already exists* — never actually runs; instead Flyway inserts a `BASELINE`-typed row claiming version 1 is already accounted for, and only `V2__add_status_column.sql` executes for real: ``` installed_rank | version | description | type | success ---------------+---------+-------------------------------------------+----------+-------- -1 | NULL | << Flyway Schema History table created >> | TABLE | true 1 | 1 | pre-flyway schema | BASELINE | true 2 | 2 | add status column | SQL | true ``` (from [`docs/output/05-flyway-baseline.txt`](output/05-flyway-baseline.txt)) The pre-existing row survives untouched, and gains the new column: ``` ID | OWNER | STATUS ---+-----------------+------- 1 | pre-flyway-data | ACTIVE ```
Trap: the baseline row's description is not a fixed string. It is exactly whatever spring.flyway.baseline-description was set to — in this test, literally "pre-flyway schema". A different, fixed marker — << Flyway Schema History table created >> — belongs to a different row (rank -1, type TABLE), created the moment the history table itself is created, whether or not baselining ever happens. Confusing the two is an easy way to write a broken assertion or a broken monitoring query — this module's own first draft did exactly that.
## Going deeper - **`baselineVersion` decides what "already accounted for" means**, and it matters which value you pick — chapter [14](14-running-both-at-once.md) shows the same mechanism used with `baseline-version=0` instead of `1`, for a database whose existing schema *doesn't* match any of your migration files at all. - [Flyway `baseline` command reference](https://documentation.red-gate.com/flyway/reference/commands/baseline) (`rel="nofollow"`) — the one-time `flyway baseline` CLI/API call this test's `baselineOnMigrate=true` triggers automatically on first startup. - Baselining is a one-way door in the sense that matters: once version 1 is marked as baselined, Flyway will never again check whether the schema it describes actually matches `V1__init.sql`'s content — that specific file's checksum is simply never looked at again for this database.