Deploying Kotlin on Heroku with Ktor

Kotlin reminds me of a young Harry Potter. It’s fresh, full of zeal, and has the support of a great institution. Harry had Hogworts, but Kotlin has the entire JVM ecosystem to nurture its growth.

Like Java, Kotlin is a statically typed, compiled language. But it differs from Java by sporting many advanced features borrowed from other JVM languages and introducing new capabilities like coroutines and null safety.

Clicking this button is the fastest way to get started with Kotlin. It deploys a simple Ktor based web app on Heroku (for free):

Deploy to Heroku

Many thanks go to Ilya Ryzhenkov for putting this together. The Kotlin community is vibrant and extremely helpful. I chatted with Ilya in the Kotlin Slack group where over six thousand people are hanging out.

You can dive into the source code for that example if you’d like (it serves up HTML and accesses a database), but we’ll take a look at another more interesting example to see how a Kotlin web application running on Heroku is put together.

Using Coroutines

The Ktor repository contains a few sample apps that demonstrate both basic and unique capabilities of Kotlin. An interesting one is the Ktor async sample, which uses a coroutine to perform some CPU intensive work without blocking the main thread.

I’ve extracted this example into it’s own repo, which you can clone locally by running:

$ git clone https://github.com/kissaten/ktor-samples-async/

The main function in the app uses the experimental async feature to call a suspending function, handleLongCalculation(start: Long):

fun Application.main() {
    install(DefaultHeaders)
    install(CallLogging)
    install(Routing) {
        get("/{...}") {
            val start = System.currentTimeMillis()
            async(executor.asCoroutineDispatcher()) {
                call.handleLongCalculation(start)
            }.await()
        }
    }
}

The handleLongCalculation function is defined with the suspend keyword, which indicates that this function can be paused at certain suspension points so that the main thread of execution can perform some other work while it waits.

In this example, the suspension point is the delay call. Otherwise, the function simply calculates some random number and renders it (along with the time it took to do so).

private suspend fun ApplicationCall.handleLongCalculation(start: Long) {
  val queue = System.currentTimeMillis() - start
  var number = 0
  val random = Random()
  for (index in 0..300) {
    delay(10)
    number += random.nextInt(100)
  }

  val time = System.currentTimeMillis() - start
  respondHtml {
    head {
      title { +"Async World" }
    }
    body {
      h1 {
        +"We calculated this after ${time}ms (${queue}ms in queue): $number"
      }
    }
  }
}

If we bombard this server with requests, we’ll see that the time to cacluate these random numbers is much faster than a synchronous version of the code.

The coroutine in this example is used to prevent a CPU operation from blocking, but it can also be used to prevent blocking of network IO, file IO, and GPU operations.

Now, let’s get this app ready for the cloud.

Preparing a Ktor App for Heroku

There are only three changes required to make a typical Ktor app work on Heroku:

  • Copy dependencies into the target/ directory.
  • Set the port from the $PORT environment variable.
  • Define the web process type in a Procfile.

The dependencies are copied with the maven-dependency-plugin, which looks like this in the pom.xml.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <phase>package</phase>
      <goals><goal>copy-dependencies</goal></goals>
    </execution>
  </executions>
</plugin>

For Gradle, a similar technique is described in the Heroku Dev Center.

We can set the port in the resources/application.conf by using the $ notation to reference an environment variable, like this:

ktor {
  deployment {
    environment = development
    port = ${PORT}
  }

  application {
    modules = [ org.jetbrains.ktor.samples.async.AsyncApplicationKt.main ]
  }
}

Then we create a Procfile, and put the following line in it:

web: java -cp target/dependency/*:target/classes/ org.jetbrains.ktor.netty.DevelopmentHost

This defines a web process type that Heroku will use to run the app. It uses a simple java command to launch Ktor’s built-in Netty host with the app on the classpath.

If you’ve installed the Heroku CLI, you can run the app locally with the command:

$ heroku local web

Finally, you can deploy this app to Heroku by running:

$ heroku create
$ git push heroku master

After the deployment process is finished, run heroku open to see the app, and heroku logs to see the log output.

Other ways to deploy Kotlin

Ktor includes bindings for Jetty and Tomcat in addition to Netty, which we used here. You can even create an embedded Ktor server, which is exactly what the ktor-heroku-start example does.

But there are many other web frameworks to choose from. Spring Boot has support for Kotlin, and so does Vert.x. In fact, Kotlin integrates so well with Java APIs that it should be possible to use most Java frameworks from Kotlin.

All you need now is a wand.