Application code gets version control, automated tests, and one-click deployments. Database schemas? Often they're still managed with hand-run SQL scripts and whispered tribal knowledge. That gap between code deployment and database deployment is where things break.
Flyway is a database migration tool that brings version control principles to your schema. You write numbered SQL files, Flyway tracks what's been applied, and every environment stays in sync. Combined with DeployHQ's build pipelines, you can automate database migrations as a pre-deployment step — so your schema and code always ship together.
This guide covers how Flyway works, how to use it with JVM and non-JVM projects, and how to integrate it into your deployment workflow.
The Database Migration Problem
When database changes are managed manually, teams run into predictable problems:
- Schema drift — development, staging, and production databases diverge, causing
works on my machine
failures - Error-prone deployments — running SQL scripts by hand is tedious and one wrong statement can cause downtime
- No audit trail — no record of who changed what, when, or why
- Difficult rollbacks — reverting a schema change is far harder than reverting a code commit
Migration tools solve this by treating schema changes as versioned, sequential scripts that are applied automatically.
How Flyway Works
Flyway operates on a straightforward principle: versioned migration scripts.
- You write migration files — plain SQL files with version numbers:
V1__create_users_table.sql,V2__add_email_column.sql - Flyway tracks state — a
flyway_schema_historytable in your database records every applied migration, its checksum, and when it ran - Flyway applies the diff — on each run, it compares your migration files against the history table and applies only the pending ones, in order
- Checksums catch tampering — if an already-applied script is modified, Flyway detects the mismatch and stops, preventing silent corruption
- Every environment stays in sync — the same migrations run in dev, staging, and production, eliminating drift
Flyway for JVM Projects
Flyway originated in the Java ecosystem and integrates deeply with JVM build tools.
Java API
Run migrations programmatically — useful for applying them on application startup:
import org.flywaydb.core.Flyway;
public class DatabaseMigrator {
public static void main(String[] args) {
Flyway flyway = Flyway.configure()
.dataSource("jdbc:postgresql://localhost:5432/mydb", "user", "password")
.locations("classpath:db/migration")
.load();
flyway.migrate();
System.out.println("Database migration completed successfully!");
}
}
Maven Plugin
Add Flyway to your build lifecycle:
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>11.8.0</version>
<configuration>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<user>user</user>
<password>password</password>
<locations>
<location>classpath:db/migration</location>
</locations>
</configuration>
</plugin>
</plugins>
</build>
Run with: mvn flyway:migrate
Gradle Plugin
plugins {
id "org.flywaydb.flyway" version "11.8.0"
}
flyway {
url = 'jdbc:postgresql://localhost:5432/mydb'
user = 'user'
password = 'password'
locations = ['classpath:db/migration']
}
Run with: gradle flywayMigrate
Spring Boot Integration
Spring Boot 3.x auto-configures Flyway when you add the flyway-core dependency — migrations run automatically on startup from src/main/resources/db/migration.
Note: Spring Boot 4.x changed this — you now need spring-boot-starter-flyway instead of just flyway-core.
Transactional DDL
For databases that support it (PostgreSQL, SQL Server), Flyway wraps each migration in a transaction. If any statement fails, the entire script rolls back — no partial schema changes.
Flyway for Non-JVM Projects
Flyway's command-line interface works with any language or framework. The CLI download bundles a JRE, so you don't need Java installed separately.
1. Configure your connection
Create a flyway.conf file:
flyway.url=jdbc:mysql://localhost:3306/mydb
flyway.user=root
flyway.password=password
flyway.locations=filesystem:sql/migrations
2. Organise your migration scripts
sql/migrations/
V1__create_products_table.sql
V2__add_price_column.sql
V3__create_orders_table.sql
Version numbers can use dots or underscores as separators (V2.1__ and V2_1__ are both valid).
3. Run migrations
flyway -configFiles=flyway.conf migrate
4. Integrate with CI/CD
Add Flyway as a pre-deployment step in your pipeline. Here's a shell script example:
#!/bin/bash
export FLYWAY_URL="jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}"
export FLYWAY_USER="${DB_USER}"
export FLYWAY_PASSWORD="${DB_PASSWORD}"
export FLYWAY_LOCATIONS="filesystem:./db/migrations"
echo "Running Flyway migrations..."
./flyway migrate
if [ $? -eq 0 ]; then
echo "Flyway migrations completed successfully!"
else
echo "Flyway migrations failed!"
exit 1
fi
In DeployHQ, you can run this as a build pipeline command or SSH pre-deployment hook, with database credentials stored securely as environment variables.
Key Features
- Plain SQL migrations — write standard SQL, no proprietary syntax required
- Checksum validation — prevents silent modifications to already-applied scripts
- Baseline support — bring an existing database under Flyway control without replaying history
- Validation — detect inconsistencies between your scripts and database state with
flyway validate - Clean command — wipe schemas for dev/test environments. Warning: this drops ALL objects in configured schemas. Never use in production. Set
flyway.cleanDisabled=truein production configs - Undo migrations — available in the Enterprise edition. The free Community edition requires manual rollback scripts
- 50+ databases supported — MySQL, PostgreSQL, Oracle, SQL Server, SQLite, MariaDB, CockroachDB, Snowflake, and many more
Best Practices
- One change per migration — each script should be a single, atomic schema change. Don't bundle unrelated changes
- Descriptive names —
V3__add_index_on_users_email.sqlis better thanV3__update.sql - Test before production — run migrations in staging first. Tools like Testcontainers can spin up isolated databases for automated testing
- Design for backward compatibility — avoid destructive changes (dropping columns) if the current app version depends on them. Use a phased approach: deprecate first, remove in a later migration
- Commit migrations to Git — store them alongside your application code so schema history tracks with your codebase. Deploy from GitHub or GitLab and DeployHQ picks them up automatically
- Automate with CI/CD — run migrations as a pre-deployment step, not manually. DeployHQ's build pipelines handle this natively
- Use minimal database privileges — create a dedicated migration user with only the permissions Flyway needs
- Always back up before migrating production — Flyway is reliable, but backups are non-negotiable
How Flyway Works with DeployHQ
DeployHQ supports Flyway through its build pipeline and hook system:
- Build pipelines — run
flyway migrate(or Maven/Gradle commands) as a build step before files are deployed - Pre-deployment SSH hooks — execute migrations directly on your server before the new code goes live
- Environment variables — store
DB_HOST,DB_USER,DB_PASSWORDsecurely without hardcoding credentials - Deployment history — full visibility into every deployment, including migration outcomes
- One-click rollback — revert your application deployment if a migration causes issues
- Deploy from GitHub, GitLab, or Bitbucket — your migration scripts deploy alongside your code from any provider
Flyway vs Alternatives
Flyway isn't the only migration tool. Here's how it compares:
| Tool | Ecosystem | Key Difference |
|---|---|---|
| Liquibase | Java / cross-platform | Supports XML/YAML/JSON changelogs in addition to SQL. More enterprise-oriented |
| Alembic | Python (SQLAlchemy) | Auto-generates migrations from model changes. Python-native |
| Prisma Migrate | Node.js / TypeScript | Schema-first approach with auto-generated SQL |
| Atlas | Go / cross-platform | Declarative schema-as-code with HCL syntax |
| Django Migrations | Python (Django) | Built into the framework, auto-detects model changes |
Flyway's strength is its simplicity: plain SQL files, no proprietary format, and it works with any language via the CLI.
Licensing
Flyway offers two editions:
- Community (free, open source) — core migration features, checksum validation, CLI + Maven + Gradle support
- Enterprise (paid) — adds undo migrations, dry runs, Oracle SQL*Plus support, and priority support from Redgate
The free Community edition covers the majority of use cases.
FAQ
Q: Does Flyway work with MySQL? A: Yes. Flyway supports 50+ databases including MySQL, PostgreSQL, Oracle, SQL Server, SQLite, MariaDB, and CockroachDB. See the full list.
Q: Do I need Java installed to use Flyway? A: Not if you use the CLI — it bundles a JRE. For JVM projects using Maven/Gradle plugins, you'll already have Java in your build environment.
Q: Can I use Flyway with a Node.js or Python project? A: Yes. Use the Flyway CLI to run migrations from any language. Write your migrations in plain SQL and integrate the CLI into your build scripts.
Q: Is Flyway free? A: The Community edition is free and open source. The Enterprise edition adds advanced features like undo migrations. See Redgate's pricing for details.
Q: What happens if I accidentally modify an already-applied migration?
A: Flyway detects the checksum mismatch and halts, preventing further migrations. You can use flyway repair to update the stored checksum if the change was intentional.
Database migrations don't have to be a manual, error-prone process. Flyway gives you version-controlled, repeatable schema changes — and when paired with DeployHQ, those migrations run automatically as part of every deployment.
Start your free DeployHQ trial and automate your database migrations alongside your code deployments. See pricing for team plans.
Questions? Reach out at support@deployhq.com or @deployhq.