DEV Community

Vinicius Carvalho
Vinicius Carvalho

Posted on

3 2

Deploying fatjar Ktor applications on Google Cloud App Engine

The original serverless platform

Google App Engine or GAE for short. Has always been an amazing service to run your web applications. Some highlights:

  • Supports multiple runtimes (java, python, go, php, ruby, nodejs )
  • Serverless execution mode, pay only for requests in flight
  • Offers free tier for some services
  • Support for versioning and traffic splitting
  • Task scheduling and queuing of background tasks
  • Many more, visit the official documentation for more information.

However for java developers there have always been a problem, you could only deploy war files, restricting you to the Servlet Spec.

Not anymore, starting with the java11 runtime, GAE now offers a way to run self contained jar files (Ktor, SpringBoot, Micronaut, Quarkus).

Introducing the Java11 runtime

Starting with the java11 runtime, you can now deploy a "fat jar" file and execute it instead of deploying a war file into a Jetty container.

All you need to do is create a new app.yaml file that contains the correct entrypoint command:

runtime: java11
instance_class: F2
entrypoint: 'java  -jar frontend-1.0-SNAPSHOT-all.jar'
service: default

You can pass any valid java argument to the JRE and if you would like to learn all the possible options of the yaml file, check the documentation

Skelleton project for ktor on GAE

Today I'll share my skelleton project for running ktor apps on GAE. Please feel free to clone it from here and use it as starting point.

Project layout

.
├── build.gradle
├── gradle
│   └── wrapper
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   ├── appengine
    │   ├── java
    │   ├── kotlin
    │   └── resources
    └── test
        ├── java
        ├── kotlin
        └── resources

I've always liked the standard gradle project layout. The only difference here is the appengine folder under our main directory. This contains the app.yaml deployment file.

Gradle configuration

First off, this project uses gradle 5.6. Your gradle/wrapper/gradle-wrapper.properties should look like this:

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

The build.gradle is generic enough that you can literally just copy and paste and use it for your project. As I mentioned before this is a template I use for all my projects.

buildscript {
    ext.kotlin_version = '1.3.50'
    ext.ktor_version = '1.2.4'
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
        classpath 'com.google.cloud.tools:appengine-gradle-plugin:1.+'
    }
}


apply plugin: 'kotlin'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.google.cloud.tools.appengine'

group 'io.igx.cloud'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

mainClassName = "io.ktor.server.netty.EngineMain"

repositories {
    mavenCentral()
    jcenter()
}


appengine {
    stage {
        artifact = "build/libs/$project.name-$project.version-all.jar"
    }
}

test {
    useJUnitPlatform()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation "io.ktor:ktor-server-core:$ktor_version"
    implementation "io.ktor:ktor-server-netty:$ktor_version"
    implementation "io.ktor:ktor-gson:$ktor_version"


    testImplementation "org.assertj:assertj-core:3.13.2"
    testImplementation "io.mockk:mockk:1.9.3"
    testImplementation "org.junit.jupiter:junit-jupiter:5.5.1"
    testImplementation 'org.koin:koin-test:2.0.1'

}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

I'm using shadow plugin to create my fat jar. It integrates nicely on my application gradle phase and generates an artifact appending -all to its name.

I've also modified the appengine defaults to point to the new jar created via the shadow plugin.

And last, I added the gradle appengine plugin.

Very simple, but reusable and saves me a ton of time. To build/deploy

./gradlew clean build
./gradlew appengineDeploy

That's it, hope you enjoy it.

Happy coding

Sentry mobile image

Mobile Vitals: A first step to Faster Apps

Slow startup times, UI hangs, and frozen frames frustrate users—but they’re also fixable. Mobile Vitals help you measure and understand these performance issues so you can optimize your app’s speed and responsiveness. Learn how to use them to reduce friction and improve user experience.

Read the guide →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay