Flyway is a popular database migration tool that is commonly used in JVM environments.

Quarkus provides first class support for using Flyway as will be explained in this guide.

Setting up support for Flyway

As shown in the Developing with Flyway section, to start using Flyway with your project, you just need to:

  • add your migrations to the src/main/resources/db/migration folder as you usually do with Flyway

  • activate the migrate-at-start option to migrate the schema automatically or inject the Flyway object and run your migration as you normally do

In your build file, add the following dependencies:

  • the Flyway extension

  • your JDBC driver extension (quarkus-jdbc-postgresql, quarkus-jdbc-h2, quarkus-jdbc-mariadb, …​)

  • unless you’re using in-memory or file databases (such as H2 or SQLite), you need to add a flyway module dependency corresponding to the database you’re using. (for more details)

pom.xml
<!-- Flyway specific dependencies -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-flyway</artifactId>
</dependency>

<!-- JDBC driver dependencies -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>

<!-- Flyway SQL Server specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-sqlserver</artifactId>
</dependency>

<!-- Flyway MariaDB/MySQL specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-mysql</artifactId>
</dependency>

<!-- Flyway Oracle specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-oracle</artifactId>
</dependency>

<!-- Flyway PostgreSQL specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-postgresql</artifactId>
</dependency>

<!-- Flyway DB2 specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-db2</artifactId>
</dependency>

<!-- Derby specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-derby</artifactId>
</dependency>

<!-- HSQLDB specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-hsqldb</artifactId>
</dependency>

<!-- Informix specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-informix</artifactId>
</dependency>

<!-- Redshift specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-redshift</artifactId>
</dependency>

<!-- Saphana specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-saphana</artifactId>
</dependency>

<!-- Snowflake specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-snowflake</artifactId>
</dependency>

<!-- Sybasease specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-sybasease</artifactId>
</dependency>

<!-- Firebird specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-firebird</artifactId>
</dependency>

<!-- BigQuery specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-gcp-bigquery</artifactId>
</dependency>

<!-- Spanner specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-gcp-spanner</artifactId>
</dependency>

<!-- Singlestore specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-singlestore</artifactId>
</dependency>
build.gradle
// Flyway specific dependencies
implementation("io.quarkus:quarkus-flyway")
// JDBC driver dependencies
implementation("io.quarkus:quarkus-jdbc-postgresql")
// Flyway SQL Server specific dependencies
implementation("org.flywaydb:flyway-sqlserver")
// Flyway MariaDB/MySQL specific dependencies
implementation("org.flywaydb:flyway-mysql")
// Flyway Oracle specific dependencies
implementation("org.flywaydb:flyway-database-oracle")
// Flyway PostgreSQL specific dependencies
implementation("org.flywaydb:flyway-database-postgresql")
// Flyway DB2 specific dependencies
implementation("org.flywaydb:flyway-database-db2")
// Flyway Derby specific dependencies
implementation("org.flywaydb:flyway-database-derby")
// HSQLDB specific dependencies
implementation("org.flywaydb:flyway-database-hsqldb")
// Informix specific dependencies
implementation("org.flywaydb:flyway-database-informix")
// Redshift specific dependencies
implementation("org.flywaydb:flyway-database-redshift")
// Saphana specific dependencies
implementation("org.flywaydb:flyway-database-saphana")
// Snowflake specific dependencies
implementation("org.flywaydb:flyway-database-snowflake")
// Sybasease specific dependencies
implementation("org.flywaydb:flyway-database-sybasease")
// Firebird specific dependencies
implementation("org.flywaydb:flyway-firebird")
// BigQuery specific dependencies
implementation("org.flywaydb:flyway-gcp-bigquery")
// Spanner specific dependencies
implementation("org.flywaydb:flyway-gcp-spanner")
// Singlestore specific dependencies
implementation("org.flywaydb:flyway-singlestore:10.15.0")

Flyway support relies on the Quarkus datasource config. It can be customized for the default datasource as well as for every named datasource. First, you need to add the datasource config to the application.properties file in order to allow Flyway to manage the schema. Also, you can customize the Flyway behaviour by using the following properties:

Developing with Flyway

The following is an example for the application.properties file:

# configure your datasource
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=sarah
quarkus.datasource.password=connor
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase

# Run Flyway migrations automatically
quarkus.flyway.migrate-at-start=true

# More Flyway configuration options
# quarkus.flyway.baseline-on-migrate=true
# quarkus.flyway.baseline-version=1.0.0
# quarkus.flyway.baseline-description=Initial version
# quarkus.flyway.connect-retries=10
# quarkus.flyway.schemas=TEST_SCHEMA
# quarkus.flyway.table=flyway_quarkus_history
# quarkus.flyway.locations=db/location1,db/location2
# quarkus.flyway.sql-migration-prefix=X
# quarkus.flyway.repeatable-sql-migration-prefix=K

Add a SQL migration to the default folder following the Flyway naming conventions: src/main/resources/db/migration/V1.0.0__Quarkus.sql

CREATE TABLE quarkus
(
  id   INT,
  name VARCHAR(20)
);
INSERT INTO quarkus(id, name)
VALUES (1, 'QUARKED');

Now you can start your application and Quarkus will run the Flyway’s migrate method according to your config.

With quarkus.flyway.migrate-at-start=true, as in the example above, Quarkus will execute the Flyway migration as part of the application startup.
@ApplicationScoped
public class MigrationService {
    // You can Inject the object if you want to use it manually
    @Inject
    Flyway flyway; (1)

    public void checkMigration() {
        // This will print 1.0.0
        System.out.println(flyway.info().current().getVersion().toString());
    }
}
1 Inject the Flyway object if you want to use it directly
In dev mode Quarkus will automatically restart the application if any of the existing migration scripts get modified. If you want to take advantage of this while developing and testing new migration scripts, you will want to set %dev.quarkus.flyway.clean-at-start=true, so that Flyway actually runs the modified migration.

Repairing the Flyway schema history table

There are different scenarios which may require repairing the Flyway schema history table. One such scenario is when a migration fails in a database which doesn’t support transactional DDL statements.

In such situations the Flyway repair command comes in handy. In Quarkus this can either be executed automatically before the migration by setting quarkus.flyway.repair-at-start=true or manually by injecting the Flyway object and calling Flyway#repair().

Multiple datasources

Flyway can be configured for multiple datasources. The Flyway properties are prefixed exactly the same way as the named datasources, for example:

quarkus.datasource.db-kind=h2
quarkus.datasource.username=username-default
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:default
quarkus.datasource.jdbc.max-size=13

quarkus.datasource.users.db-kind=h2
quarkus.datasource.users.username=username1
quarkus.datasource.users.jdbc.url=jdbc:h2:tcp://localhost/mem:users
quarkus.datasource.users.jdbc.max-size=11

quarkus.datasource.inventory.db-kind=h2
quarkus.datasource.inventory.username=username2
quarkus.datasource.inventory.jdbc.url=jdbc:h2:tcp://localhost/mem:inventory
quarkus.datasource.inventory.jdbc.max-size=12

# Flyway configuration for the default datasource
quarkus.flyway.schemas=DEFAULT_TEST_SCHEMA
quarkus.flyway.locations=db/default/location1,db/default/location2
quarkus.flyway.migrate-at-start=true

# Flyway configuration for the "users" datasource
quarkus.flyway.users.schemas=USERS_TEST_SCHEMA
quarkus.flyway.users.locations=db/users/location1,db/users/location2
quarkus.flyway.users.migrate-at-start=true

# Flyway configuration for the "inventory" datasource
quarkus.flyway.inventory.schemas=INVENTORY_TEST_SCHEMA
quarkus.flyway.inventory.locations=db/inventory/location1,db/inventory/location2
quarkus.flyway.inventory.migrate-at-start=true

Notice there’s an extra bit in the key. The syntax is as follows: quarkus.flyway.[optional name.][datasource property].

Without configuration, Flyway is set up for every datasource using the default settings.

Customizing Flyway

In cases where Flyway needs to be configured in addition to the configuration options that Quarkus provides, the io.quarkus.flyway.FlywayConfigurationCustomizer class comes in handy.

To customize Flyway for the default datasource, simply add a bean like so:

@Singleton
public static class MyCustomizer implements FlywayConfigurationCustomizer {

    @Override
    public void customize(FluentConfiguration configuration) {
        // do something with configuration
    }
}

When named datasources are used, the @FlywayDataSource annotation can be used to specify the datasource to which the customizer applies. For example, if there are multiple datasources one of which is called users and customization of Flyway is needed for only that datasource, then the following code can be used:

@Singleton
@FlywayDataSource("users")
public static class UsersCustomizer implements FlywayConfigurationCustomizer {

    @Override
    public void customize(FluentConfiguration configuration) {
        // do something with configuration
    }
}

Using the Flyway object

In case you are interested in using the Flyway object directly, you can inject it as follows:

@ApplicationScoped
public class MigrationService {
    // You can Inject the object if you want to use it manually
    @Inject
    Flyway flyway; (1)

    @Inject
    @FlywayDataSource("inventory") (2)
    Flyway flywayForInventory;

    @Inject
    @Named("flyway_users") (3)
    Flyway flywayForUsers;

    public void checkMigration() {
        // Use the flyway instance manually
        flyway.clean(); (4)
        flyway.migrate();
        // This will print 1.0.0
        System.out.println(flyway.info().current().getVersion().toString());
    }
}
1 Inject the Flyway object if you want to use it directly
2 Inject Flyway for named datasources using the Quarkus FlywayDataSource qualifier
3 Inject Flyway for named datasources
4 Use the Flyway instance directly

Flyway and Hibernate ORM

When using Flyway together with Hibernate ORM, you can use the Dev UI to generate the initial schema creation script.

You can find more information about this feature in the Hibernate ORM guide.

Flyway and Reactive datasources

Flyway internally relies on a JDBC datasource, whereas reactive use cases will rely on reactive SQL clients, either directly or through Hibernate Reactive. This is not a problem in Quarkus, because a single configured datasource can be made available both through reactive clients and JDBC.

To use Flyway on a datasource you otherwise access reactively, simply make sure to configure that datasource both as JDBC and reactive. This involves in particular adding dependencies to Quarkus extensions for both the JDBC driver and the reactive client, for instance quarkus-jdbc-postgresql and quarkus-reactive-pg-client.

Flyway on Kubernetes

Sometimes, it’s helpful not to execute Flyway initialization on each application startup. One such example is when deploying

on Kubernetes, where it doesn’t make sense to execute Flyway on every single replica. Instead it’s desirable to execute it once and then start the actual application without Flyway. To support this use case, when generating manifests for Kubernetes the generated manifests contain a Kubernetes initialization Job for Flyway. The Job performs initialization and the actual Pod, will starts once the Job is successfully completed.

Disabling

The feature is enabled by default and can be globally disabled, using:

quarkus.kubernetes.init-task-defaults.enabled=false

or on OpenShift:

quarkus.openshift.init-task-defaults.enabled=false

Using a custom image that controls waiting for the Job

To change the wait-for image which by default is groundnuty/k8s-wait-for:no-root-v1.7 you can use:

quarkus.kubernetes.init-task-defaults.wait-for-container.image=my/wait-for-image:1.0

or on OpenShift:

quarkus.openshift.init-task-defaults.wait-for-container.image=my/wait-for-image:1.0

Note: In this context globally means for all extensions that support init task externalization.

Known Issues in Flyway

Oracle: Multiple schemas in Dev Services

When having multiple schemas in Oracle, you can use the quarkus.flyway.schemas property to specify the schemas that Flyway should manage. However, because this is executed in the DB as the quarkus user, you need to either give DBA privileges to the user that is executing the migration (quarkus) or perform the necessary DDLs before the Dev Service starts. This is done with the quarkus.datasource.devservices.init-privileged-script-path configuration.

Giving DBA privileges to the user

  • In your application, create a SQL file named 001-devservice-init.sql (for ordering purposes, it can be any name really) in your src/main/resources with the following content:

ALTER SESSION SET CONTAINER=quarkus;
GRANT DBA TO quarkus;
  • Add the following configuration to your application.properties:

quarkus.datasource.devservices.init-privileged-script-path=001-devservice-init.sql