DEV Community

loading...
Cover image for Publishing server-side Kotlin applications: Ktor on Heroku
Kotlin

Publishing server-side Kotlin applications: Ktor on Heroku

sebastianaigner profile image Sebastian Aigner Updated on ・9 min read

Getting an application into the hands of the first users is the best feeling. Seeing them try out an app for the first time and collecting feedback from them for future improvement has always felt very rewarding to me. For server-side apps, this usually means getting the app deployed somewhere – published on the web.

Today I want to share one quick way to get Kotlin applications built with Ktor onto the web: Using Heroku.

As a PaaS (Platform as a Service), Heroku takes care of most administrative tasks so that we don't have to. Stuff like keeping their platform up-to-date and secure, managing the networking stack, and more.

I personally enjoy Heroku because their free tier means I can publish apps at no upfront cost. Plus, I also get to say that my application runs in the cloud from day one! 😉🌩

If you also want to learn how to deploy your Kotlin app directly from a local Git repository, take 10 minutes and let me guide you through the steps for getting your first Kotlin app online on Heroku!

Signing up for Heroku & Setting up the Heroku CLI 🛠

Before we even create our demo Kotlin application, it's a good idea to set up our environment – starting with setting up a Heroku account. We can do that by simply visiting their signup page at http://signup.heroku.com/. The free plan which is provided by default is more than enough for our purposes (even with its limitations), so we don't need to add any payment information.

Heroku Signup Page

After creating our account, we next install the Heroku Command Line Interface (CLI), which will allow us to create and manage our Heroku applications.

Installation instructions for the Heroku CLI vary slightly between operating systems, so it’s best to follow the official and up-to-date instructions on Heroku’s website for their setup.

To verify that our installation was successful and to authenticate the CLI with our account which we created a few paragraphs ago, we run the heroku command on our terminal once and follow the on-screen instructions.

With this one-time setup step out of the way, we are ready to prepare our application!

Heroku CLI

Creating our Ktor application ✨

Let’s create a quick sample application which we will afterward configure to be deployed. If you already have your own Ktor project which you want to get deployed to Heroku, you can move directly to the next section.

There are multiple easy ways to create a new Kotlin project with Ktor. For example, we can use the online Ktor Project Generator, or the Ktor IntelliJ IDEA plugin – and in fact, both tools even expose the same configuration options. For this example, we are going to configure our project via the Ktor IntelliJ IDEA plugin, which can be accessed through File | New… | Project after it has been installed.

To follow along with the code snippets, enable the “Gradle Kotlin DSL” option for the project, and add the “Routing” feature.

Ktor Project Wizard

On the pages that follow, we input a project name and artifact ID of our choice.

After the Gradle import has finished, we actually already have a ready-to-run “Hello World” project powered by Ktor. We can inspect its source code by navigating to the Application.kt file in our src directory, where we should see something like this:

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

@Suppress("unused") // Referenced in application.conf
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
    routing {
        get("/") {
            call.respondText("HELLO WORLD!", contentType = ContentType.Text.Plain)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

As we can see, this simple Ktor program defines one route, /, which responds to HTTP GET requests with the words HELLO WORLD!.

By pressing the and pressing the “Run” button in the gutter next to the main function, and navigating to http://localhost:8080/, we can try the application out locally:

Hello World

Now, it's time to set get our little application configured for running on Heroku's cloud!

Configuring our Ktor application for Heroku deployment ⚙️

To deploy our application to Heroku, our application needs to do three things:

  • It should respect the PORT environment variable: When starting our app, Heroku assigns our application a port on which to listen for incoming requests, and Heroku’s routers then take care of bringing the HTTP traffic to our application on that port.
  • It should provide a stage task: When we deploy our application, Heroku runs the Gradle task called stage to turn our program into an executable.
  • It should provide a Procfile: When Heroku starts our application to handle incoming requests, this file specifies the command(s) which will be run – in our case, starting the compiled application.

The PORT environment variable

We're in luck here – the application generated by the Ktor wizard already configures our application to respect the PORT environment variable. We can see this in the application.conf in the resources directory of our project. This HOCON file is responsible for the basic behavior of our application. And, as we can see, it sets the default port of our application to 8080, optionally overriding it with the content of the PORT environment variable when it is present:

ktor {
   deployment {
       port = 8080
       port = ${?PORT}
   }
   application {
       modules = [ io.sebi.ApplicationKt.module ]
   }
}
Enter fullscreen mode Exit fullscreen mode

This is exactly what we want: When developing locally, our application is always assigned the same port (8080), and when deploying to a system where a PORT environment variable is set, like Heroku, the platform settings are respected.

The stage Gradle task

When deploying any type of application to Heroku, the platform runs a so-called buildpack – a tool that turns the source code of our project into the executable which eventually gets run on Heroku’s platform. For Gradle applications, Heroku’s buildpack looks for a stage task to create executable artifacts from our code. Luckily for us, the already preconfigured Gradle application plugin already comes with a task called installDist which does exactly that.

All we need to do is create an alias for the installDist task called stage. If you have picked the Gradle Kotlin DSL as was suggested earlier in the tutorial, you can simply add this snippet at the bottom of the project's build.gradle.kts file:

tasks.create("stage") {
   dependsOn("installDist")
}
Enter fullscreen mode Exit fullscreen mode

The Procfile

Heroku’s Procfile mechanism tells the platform which application should be executed when our application is started.

Essentially, we want to point this file at the output generated by the stage task, which we set up in the previous step. At the point when the Procfile gets invoked, our stage task will have built our application and created a launch script in build/install/projectName/bin/projectName. We can simply point to this script to get our application running.

In the root of our project, let’s create a file called Procfile and add the following content to it (substituting projectName for, you guessed it, the name of our project):

web: ./build/install/projectName/bin/projectName
Enter fullscreen mode Exit fullscreen mode

(If you don't remember the exact name you gave your project, you can peek into settings.gradle.kts, or run the stage task once, and investigate the folder structure inside build/install.)

Our application is now set up for deployment on Heroku! At this point, the only thing standing between us and a published app is the actual deployment process itself.

Actually deploying our application 📦

Setting up a local Git repository

The primary method for getting the source code of our application onto Heroku is via Git. The simplest way for this is to use a local Git repository. Using the Heroku CLI, we can then link it to a special heroku remote repository, which will trigger the build and deployment process in the cloud.

To link everything up, we first need to create a local repository for our project, which can do directly from IntelliJ IDEA with the command VCS | Enable Version Control Integration...

Enable VCS integration

Next, we commit our project locally. In IntelliJ IDEA, Navigate to the “Commit” tool window on the left side, and commit all unversioned files. (Don’t worry about manually excluding the /build or /idea folders – the Ktor project wizard already auto-generated a .gitignore file for us which takes care of them.)

IntelliJ IDEA Commit window

Creating the Heroku application & Deploying! 🚀

Almost there! The only thing remaining is to create a Heroku app in which our new application will live. We use the Heroku CLI from the builtin terminal in IntelliJ IDEA (or a terminal of our choice) to set up the application, by executing the following command in the root folder of our project:

heroku create myprojectname
Enter fullscreen mode Exit fullscreen mode

After a few seconds, the terminal command should exit and confirm the successful creation of our project – including a preview of the URL at which our project will be visible in just a few minutes.

Creating ⬢ myprojectname... done
https://myprojectname.herokuapp.com/ | https://git.heroku.com/myprojectname.git
Enter fullscreen mode Exit fullscreen mode

In the background, this command did two things which we should care about:

  • It created a new Heroku application, which we can inspect in our web dashboard
  • It added a new Git remote called heroku to our local Git repository.

Now, we can kick off our deployment: we push the commit we made in the previous section to the newly added heroku Git remote. In IntelliJ IDEA, we can do this via VCS | Git | Push….

Push window in IntelliJ IDEA

After confirming the push, we can sit back and watch our application ascend to the cloud!

This push is going to take longer than you might be used to, because Heroku actually builds our application remotely while pushing. If you’re interested in following along with the progress, you can open the “Git” tool window at the bottom of IntelliJ IDEA to see what is happening in the background.

If everything has gone according to plan, you will see a confirming message in the logs:

remote:        BUILD SUCCESSFUL in 1m 34s        
remote:        6 actionable tasks: 6 executed        
remote: -----> Discovering process types        
remote:        Procfile declares types -> web        
remote: 
remote: -----> Compressing...        
remote:        Done: 64.3M        
remote: -----> Launching...        
remote:        Released v3        
remote:        https://myprojectname.herokuapp.com/ deployed to Heroku        
remote: 
remote: Verifying deploy... done.  
Enter fullscreen mode Exit fullscreen mode

We can see the URL for our project in this log. When navigating to this link, we see our application in all its glory, running in the cloud. Grab your friends, open the link on your phone, and tell your relatives who live across the country to click the link – you’ve just successfully deployed your Kotlin application to the world wide web!

Hello, Heroku!

What’s next?

Now that we are done with setting up our Ktor application for deployment on Heroku, we have a simple workflow for publishing new changes to the web:

  • Make the change
  • Commit the change
  • Push the change to heroku master

...and after just a minute or two, our changes will be published.

If we’re planning to collaborate with others, we might want to share our project on GitHub. In this case, we can simply create an empty repository on GitHub, add it as a remote, and push our changes to it, just as we would normally do.

If we’re feeling particularly fancy, we can use Heroku’s GitHub integration to make the deployment of our Kotlin applications happen automatically whenever we push our commits to GitHub. With this, we actually have a convenient way of doing continuous delivery (CD).

And of course, there is a lot more to explore around the Heroku platform and our server-side Kotlin application. Heroku’s “Add-ons” page allows us to add a ton of different services to your app, which we can use from our code. While many of them are paid add-ons, many popular services also offer a free “developer” tier, so you can experiment to your heart’s content. If you want to explore more fine-grained control about the JVM execution environment available on the platform, you can explore JDK- and JVM related adjustments, such as providing a system.properties file to specify a differing Java version to run your app on.

Thank you for reading!

Thanks for sticking around until the end! I hope you found this tutorial helpful. If you have any questions or ideas about what to cover next, please let me know!

Discussion (10)

pic
Editor guide
Collapse
andresantospt profile image
André L. Santos

Thanks for the nice tutorial!
I'm having a lot of trouble with application.conf, where to place it, so that Heroku sees it. I can run the App in IntelliJ, but when using Heroku I get:
Exception in thread "main" java.lang.IllegalArgumentException: Neither port nor sslPort specified. Use command line options -port/-sslPort or configure connectors in application.conf

The image shows my project structure.

Any hints to overcome this?

Collapse
sebastianaigner profile image
Sebastian Aigner Author • Edited

Unfortunately I can't see an image, but if you're using the structure generated by the wizard, your application.conf should have been automatically placed in the right directory – resources/main/application.conf. Generally, application.conf needs to be inside your resources directory.

You could also trying running the application locally through your gradle wrapper (i.e. ./gradlew run, and see if that works. If not, it might be indicative of some other issue.

Collapse
andresantospt profile image
André L. Santos

thanks for quick answer!
(at the end the image did not attach... i'm new here)
perhaps like this is easier:
github.com/andre-santos-pt/qlciscte

gradlew run works well locally.

gradle stage, compiles ok, but when running the build I get that port error. (and the same when uploading to heroku)

Thread Thread
andresantospt profile image
André L. Santos

I forgot to mention that when generating a project like in your tutorial, it goes fine. my difference is that i'm doing the multi-platform Kotlin/KotlinJs

Thread Thread
sebastianaigner profile image
Sebastian Aigner Author • Edited

Thanks for sharing the project. I investigated this by running installDist locally, which creates a "start script" in /build/install/qlciscte/bin – and that indeed throws a IllegalArgumentException: Neither port nor sslPort specified.

And in fact, you're actually hitting a bug here! More specifically, KT-37964 Gradle application/distribution plugin does not copy and name jar files correctly when using installDist task for multiplatform project.

The TLDR: installDist generates a script with an entry point using your projects .jar, which the isn't properly being copied. The workaround:

distributions {
    main {
        contents {
            from("$buildDir/libs") {
                rename("${rootProject.name}-jvm", rootProject.name)
                into("lib")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Hope this will be fixed in due time. Tripped me up as well, as you can see :)

You can also find a working Gradle configuration in the companion project for the "Building a Full Stack Web App with Kotlin Multiplatform" hands-on tutorial.

Thread Thread
andresantospt profile image
André L. Santos

now it works when I run the binary produced by gradle stage. however, when uploading to Heroku, the app still crashes. Here's the log entry:
2021-01-28T11:18:09.671617+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=qlciscte.herokuapp.com request_id=fbea81df-588f-443f-a520-7072d3110169 fwd="94.63.156.9" dyno= connect= service= status=503 bytes= protocol=https

It's not very informative. Do you know any trick to get a more detailed trace of what went wrong on Heroku?

Thread Thread
sebastianaigner profile image
Sebastian Aigner Author

Discovered the issue by running heroku logs --tail before actually triggering a build. That gave me:

2021-01-28T12:13:20.330000+00:00 app[web.1]: bash: build/install/untitled/bin/untitled: No such file or directory
Enter fullscreen mode Exit fullscreen mode

You might want to adjust the contents of your Procfile, since your project is no longer called untitled 😉

Thread Thread
andresantospt profile image
André L. Santos

meanwhile I also noticed that :) now I have another problem, but that has to with class version compilation of some libs i'm using.
thanks for the tip!

Collapse
jmfayard profile image
Jean-Michel Fayard 🇫🇷🇩🇪🇬🇧🇪🇸🇨🇴

If I have understood correctly the episode on Google Cloud Run from the podcast Talking Kotlin, Heroku's Procfile are also supported on Google Cloud Run so it should be pretty easy to run your server there as well

Collapse
jimmyflash profile image
jimmyFlash

Thank you for the detailed walk through, also first time i heard about the Ktor project generator makes the whole process a lot easier.