Runway

Kotlin (ktor)

Let’s build an application using Kotlin and the ktor framework. The application will respond to the / and /hello-world routes and listen on port 3000 by default.

Setup

initialize a new project with gradle:

# create a directory
$ mkdir ./new-project && cd new-project
$ gradle init \
--type kotlin-application \
--dsl kotlin \
--project-name runway \
--package main \
--incubating

Example app

Once the project is created, you have to edit app/src/main/kotlin/main/App.kt and replace it with the following:

package main

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun main() {
    // Get the port from the environment variable or use 3000 as default
    val port = System.getenv("PORT")?.toIntOrNull() ?: 3000

    // Start the Ktor server
    embeddedServer(Netty, port) {
        routing {
            get("/") {
                call.respondText("Hello")
            }
            get("/hello-world") {
                call.respondText("Hello World")
            }
        }
    }.start(wait = true)
}

The next step is to update the app/build.gradle.kts so we can build and run our app:

plugins {
    id("org.jetbrains.kotlin.jvm") version "1.8.10"
    id("io.ktor.plugin") version "2.3.12"
    application
}

repositories {
    mavenCentral()
    gradlePluginPortal()
}

dependencies {
    implementation("io.ktor:ktor-server-core-jvm")
    implementation("io.ktor:ktor-server-netty-jvm")
}

application {
    mainClass.set("main.AppKt")
}

Now run: ./gradlew build and it should complete. Feel free to test your app with ./gradlew run too and go to http://localhost:3000/hello-world.

Building

Commit the following multi-stage Dockerfile to your repository:

FROM gradle:8.0-jdk17 AS build

WORKDIR /workspace

# Copy the Gradle wrapper and build files
COPY app/build.gradle.kts settings.gradle.kts ./
# Copy the source code
COPY app/src ./src

RUN gradle installDist

FROM openjdk:17-jdk-slim
WORKDIR /app

# Set the user to nobody (in numeric)
USER 65534:65534

# Copy the app from the build stage
COPY --from=build /workspace/build/install/runway /app

EXPOSE 3000

CMD ["/app/bin/runway"]

The Dockerfile follows a common pattern when getting a project build and deployed: the first stage (build) is used to install all dependencies and compile the application with gradle installDist.

The second (final) stage uses a smaller image and copies the application from build and instructs Runway with a USER and a port (EXPOSE).

Deploy to Runway

Create an application on Runway:

$ runway app create
INFO    checking login status                        
INFO    created app "amazing-coffee"                 
create app amazing-coffee: done
next steps:  
* commit your changes  
* runway app deploy  
* runway open

And deploy:

$ runway app deploy && runway open

Congrats, your Kotlin app is running on Runway! ☕️