Deep-dive on the Next Gen Platform. Join the Webinar!

Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Working with Node.js
      • Troubleshooting Node.js Apps
      • Node.js Behavior in Heroku
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Working with AI
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Language Support
  • Java
  • Working with Java
  • Running Database Migrations for Java Apps

Running Database Migrations for Java Apps

English — 日本語に切り替える

Last updated December 09, 2024

Table of Contents

  • Using Liquibase
  • Using Flyway
  • Further Reading

Most database-backed applications will need to change their database schema during the course of operations. These changes are often controlled by a process called migrations or evolutions.

In this article, you’ll learn how to use two tools, Liquibase and Flyway, to run database migrations for a Java application on Heroku.

Using Liquibase

There are many ways to run Liquibase. It provides a Maven plugin, a standalone command-line tool, a Hibernate plugin, and a Spring Bean. In this article, we’ll discuss the best mechanisms for use on Heroku.

Running Liquibase automatically with Spring

Liquibase provides a very convenient SpringLiquibase bean that automatically runs your migrations on startup if they are in the correct location.

If you are using Spring Boot, you only need to include the liquibase-core dependency and put your change log in src/resources/db/changelog/db.changelog-master.yaml, as demonstrated in this Spring and Liquibase sample application.

Upon startup, you will see something like this in your logs:

2015-10-20T02:00:51.937179+00:00 app[web.1]: 2015-10-20 02:00:51.936  INFO 3 --- [main] liquibase : Successfully acquired change log lock
2015-10-20T02:00:55.419669+00:00 app[web.1]: 2015-10-20 02:00:55.419  INFO 3 --- [main] liquibase : Reading from public.databasechangelog
2015-10-20T02:00:55.571104+00:00 app[web.1]: 2015-10-20 02:00:55.555  INFO 3 --- [main] liquibase : Successfully released change log lock

However, running migrations at startup does add to your app’s boot time and may cause you to exceed the boot-timeout limit imposed by Heroku. If so, you may find that running the migrations in the Heroku release phase is preferable.

Running Liquibase with the Maven Plugin

The Liquibase migrations can be run in Heroku release phase using the Liquibase Maven Plugin by adding the following plugin configuration to your pom.xml:

<plugin>
  <groupId>org.liquibase</groupId>
  <artifactId>liquibase-maven-plugin</artifactId>
  <configuration>
    <changeLogFile>src/main/resources/db/changelog/db.changelog-master.yaml</changeLogFile>
    <url>${env.JDBC_DATABASE_URL}</url>
  </configuration>
</plugin>

Next, make sure you’re using the Maven Wrapper in your project. If you’re not already, you can add it by running:

$ mvn -N io.takari:maven:wrapper
$ git add mvnw .mvn
$ git commit -m "Added maven wrapper"

With this in place, you can add a process entry to your Procfile that will run the migrations in the release phase of deployment:

release: ./mvnw liquibase:update

However, this will require downloading all of the Liquibase dependencies each time release phase runs because the Maven .m2 cache is not included in your app’s slug. If that overhead is prohibitive you may want to run Liquibase without Maven.

Running Liquibase with the command-line tool

Your Liquibase migrations can be run in Heroku release phase using the Liquibase command-line tool. To use this tool, you’ll need to include the Liquibase JAR file in your slug by adding the following plugin configuration to your Maven pom.xml:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <phase>package</phase>
      <goals><goal>copy</goal></goals>
      <configuration>
        <artifactItems>
          <artifactItem>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
            <version>3.4.1</version>
            <destFileName>liquibase.jar</destFileName>
          </artifactItem>
          <artifactItem>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.13</version>
            <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
          </artifactItem>
          <artifactItem>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.4-1204-jdbc41</version>
            <destFileName>postgres.jar</destFileName>
          </artifactItem>
        </artifactItems>
      </configuration>
    </execution>
  </executions>
</plugin>

This will copy the JAR file to target/dependency/liquibase.jar during the Maven package goal. With this in place, you can add a process entry to your Procfile that will run the migrations in the release phase of deployment:

release: java -jar target/dependency/liquibase.jar --changeLogFile=src/main/resources/db/changelog/db.changelog-master.yaml --url=$JDBC_DATABASE_URL --classpath=target/dependency/postgres.jar update

Liquibase is not the only migration engine for Java. You can also use Flyway.

Using Flyway

Flyway provides several mechanisms for running migrations including a command-line tool, Maven plugin, Gradle plugin and an SBT plugin. In this article, we’ll discuss the best mechanisms for use on Heroku.

Running Flyway automatically with Spring

You can run Flyway automatically by including the flyway-core dependency in your app and putting your migration scripts in src/main/resources/db/migration/, as demonstrated in this Spring and Flyway sample application.

However, running migrations at startup does add to your app’s boot time and may cause you to exceed the boot-timeout limit imposed by Heroku. If this is the case, you may find that running the migrations from Heroku release phase preferable.

Running Flyway with the Maven Plugin

You can also run the Flyway Maven Plugin from Heroku’s release phase. First, add the plugin to you pom.xml:

<plugin>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-maven-plugin</artifactId>
  <configuration>
    <url>${env.JDBC_DATABASE_URL}</url>
  </configuration>
</plugin>

Next, make sure you’re using the Maven Wrapper in your project. If you’re not already, you can add it by running:

$ mvn -N io.takari:maven:wrapper
$ git add mvnw .mvn
$ git commit -m "Added maven wrapper"

With this in place, you can add a process entry to your Procfile that will run the migrations in the release phase of deployment:

release: ./mvnw flyway:migrate

However, this will require downloading all of the Flyway dependencies each time release phase runs because the Maven .m2 cache is not included in your app’s slug. If that overhead is prohibitive you may want to run Flyway without Maven.

Running Flyway with the Java API

To use the Flyway Java API, create a simple class with a main method, such as this:

package sample.flyway;

import org.flywaydb.core.Flyway;

public class Migrations {
    public static void main(String[] args) throws Exception {
        Flyway flyway = new Flyway();
        flyway.setDataSource(System.getenv("JDBC_DATABASE_URL"),
                             System.getenv("JDBC_DATABASE_USERNAME"),
                             System.getenv("JDBC_DATABASE_PASSWORD"));
        flyway.migrate();
    }
}

Then copy the flyway JAR file into your slug file by adding this plugin configuration to your pom.xml file:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <phase>package</phase>
      <goals><goal>copy</goal></goals>
      <configuration>
        <artifactItems>
          <artifactItem>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
            <version>3.2.1</version>
            <destFileName>flyway.jar</destFileName>
          </artifactItem>
          <artifactItem>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.4-1204-jdbc41</version>
            <destFileName>postgres.jar</destFileName>
          </artifactItem>
        </artifactItems>
      </configuration>
    </execution>
  </executions>
</plugin>

This will copy the JAR file to target/dependency/flyway.jar during the Maven package goal. With this in place, you can add a process entry to your Procfile that will run the migrations:

release: java -cp target/spring-boot-sample-flyway-1.0.0.jar:target/dependency/* sample.flyway.Migrations

This will run the migrations during the Heroku release phase. You can also run the migrations manually with this command:

$ heroku run release

Further Reading

For more information, see the official documentation pages for each of these open source projects:

  • Liquibase
  • Flyway

And you can read more about Connecting to Relational Databases on Heroku with Java in the Dev Center.

Keep reading

  • Working with Java

Feedback

Log in to submit feedback.

Warming Up a Java Process Run Non-web Java Dynos on Heroku

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices