Running Grails 3 on Heroku

Posted by David Estes on May 31, 2015

Filed under Grails, Open Source, Groovy

Today I decided to find out exactly how easy it was to deploy a grails 3 application to Heroku. I have never tried a deployment of a java based app to heroku (just Ruby) so this was a bit of a learning experience for myself. I have however, attempted to deploy to Cloud Foundry in a previous expirement with some success there.

Grails 3 has a much smaller memory footprint than its preceding versions and a much cleaner build architecture than before. Which should help us fit both build time and startup time within the time limit restrictions given by heroku.

Create a Grails Application

The first step is to go ahead and create a grails application. In this example I will call my app 'Quest' and use it to keep track of some ideas for Quests (seemed like it fit the theme of Grails).

#I use gvm to set my grails version
gvm use grails 3.0.1 
grails create-app quest
cd quest

Sweet now that we have a grails app lets get started. First I should create a domain class for storing my quest ideas

grails create-domain-class Quest

Now that I have created a file for storing my quests lets go ahead and set some of the properties we want to keep track of on our Quest.groovy

package quest

class Quest {
    String name
    String description
    Integer difficulty=9
    Date dateCreated
    Date lastUpdated

    static mapping = {
        description type: 'text'
    }
    static constraints = {
        name unique:true
    }
}

Ok now that thats done lets go ahead and generate some controller view code for a quick example:

grails generate-all quest.Quest

Configuring for Heroku

Now we have a basic grails app to play around with on heroku. Time to prep the build. The first thing we need to do is configure out grails app to connect to the shared Postgres database that heroku provides. The somewhat dated heroku documentation about running grails has a section that is somewhat relevant here.

First lets go ahead and create an application.groovy file in grails-app/conf By default Heroku gives us a reference to the database connection in the form of a DATABASE_URL environment variable. We can extract the elements we need to connect the dataSource provider to it via the following.

environments {
  production {
    dataSource {
      dbCreate = "update"
      driverClassName = "org.postgresql.Driver"
      dialect = org.hibernate.dialect.PostgreSQLDialect
      uri = new URI(System.env.DATABASE_URL?:"postgres://test:test@localhost/test")
      url = "jdbc:postgresql://" + uri.host + ":" + uri.port + uri.path
      username = uri.userInfo.split(":")[0]
      password = uri.userInfo.split(":")[1]
    }
  }
}

NOTE: This file will get merged with the application.yml file and not take precedence. so you should also remove the entire dataSource block from application.yml.

Next up we need to define a new task in our build.gradle file. When Heroku sees this application it will detect that it was built using gradle and use the gradle build tool configuration.

Add these dependency lines to your build.gradle:

runtime 'postgresql:postgresql:8.4-702.jdbc3'
provided "org.springframework.boot:spring-boot-starter-tomcat"

And at the bottom of my build.gradle the following should be added:


task stage() {
    dependsOn clean, war
}
tasks.stage.doLast() {
    delete fileTree(dir: "build/distributions")
    delete fileTree(dir: "build/assetCompile")
    delete fileTree(dir: "build/distributions")
    delete fileTree(dir: "build/libs", exclude: "*.war")
}
war.mustRunAfter clean

What we are doing here is setting up a new task called stage. This task will run the clean and assemble tasks in the appropriate order. For our purposes we only really need the generated war created by the build task to run the application. The rest of the files created for distribution should be removed otherwise your heroku Slug size will be way beyond the 300MB limit. By doing this your slug size should be hovering around 102MB.

Next we need to copy in the webapp-runner into our project:

mkdir server
curl http://repo2.maven.org/maven2/com/github/jsimone/webapp-runner/7.0.34.3/webapp-runner-7.0.34.3.jar > server/webapp-runner.jar

Now we need to setup the Procfile. This file tells Heroku what type of application you are running as well as how to run it. Create a file in the root of the project called Procfile:

web: cd build/libs ; java $JAVA_OPTS -Dgrails.env=prod -jar ../../server/webapp-runner.jar --port $PORT *.war

This tells the heroku runtime to run the jar file within the build/libs folder. Currently it is very important that you move the current directory because if you don't grails will detect the root grails-app folder and assume it is running in a development style mode, therefore starting up incorrectly. This is currently an issue in version 3.0.1 and should be resolved in the coming weeks.

NOTE: You may have noticed we are using a war file to run within heroku instead of the grails 3 fatJar container. This is because the fatJar can take more than 60 seconds to bind to the listening port of the dyno. If this happens Heroku will shutdown your app and mark it as crashed. By running as a war file within the webapp-runner we allow the tomcat server to bind to the port immediately and start up the application after the fact.

The last file we need to create is a .gitignore file appropriate for git:

.DS_Store
build/
.gradle

Now we need to initialize our app as a git repository and create a heroku application:

git init
git add .
git commit -am 'first commit'
heroku create
heroku addons:create heroku-postgresql

NOTE: If you have not done so already you may need to install the heroku cli by running gem install heroku.

Deploying to Heroku

Now deploying to heroku is as easy as pushing to a git repository:

git push heroku master

Now you can watch as heroku's build servers compile your project into a nice Slug that can be run across the dyno architecture. At the end of the deployment type heroku open to open your browser to the url of your running application. It may take a few minutes for your application to startup. You can check the status of the startup process by running heroku logs