Docker and Kotlin: Containerizing Applications from A to Z
Containerization has become the de facto standard for modern development. It solves the "it works on my machine" problem and simplifies CI/CD. In this article, we will explore how to effectively containerize Kotlin applications using Docker — from creating a simple Dockerfile to production optimizations and orchestration.
1. Basics: Dockerfile for a Kotlin Application
The heart of containerization is the Dockerfile. For Kotlin applications, which compile to JVM bytecode, we use multi-stage builds. This allows separating the build environment from the runtime environment, reducing the size of the final image.
Consider a typical Kotlin application using Gradle (the build.gradle.kts file is simplified):
// build.gradle.ktsplugins { kotlin("jvm") version "1.9.0" application id("com.google.cloud.tools.jib") version "3.4.0" // optional}
application { mainClass.set("com.example.MainKt")}Let's create a Dockerfile with two stages:
# Stage 1: BuildFROM gradle:8.5-jdk21 AS builderWORKDIR /appCOPY build.gradle.kts settings.gradle.kts ./COPY gradle ./gradleCOPY src ./srcRUN gradle build --no-daemon
# Stage 2: RunFROM eclipse-temurin:21-jre-alpineWORKDIR /appCOPY --from=builder /app/build/libs/my-app-*.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "app.jar"]Commands for building and running:
docker build -t my-kotlin-app .docker run -p 8080:8080 my-kotlin-appRecommendations:
- Use
eclipse-temurin(OpenJDK) instead of the full JDK — the image is smaller. - Cache dependencies: first copy
build.gradle.kts, thensrc. - For Spring Boot applications, you can use
layersfor incremental builds.
2. Optimization with Jib and Gradle
Writing a Dockerfile manually is classic, but there are tools that automate the process. Jib (from Google) integrates with Maven/Gradle and builds optimized Docker images without a Docker daemon.
Add the Jib plugin to build.gradle.kts:
plugins { id("com.google.cloud.tools.jib") version "3.4.0"}
jib { from { image = "eclipse-temurin:21-jre-alpine" } to { image = "my-registry/my-kotlin-app:latest" } container { ports = listOf("8080") jvmFlags = listOf("-Xms512m", "-Xmx1024m") }}Build with a single command:
./gradlew jibDockerBuildAdvantages of Jib:
- Automatic layer separation (dependencies, resources, classes).
- No Dockerfile required.
- Works without a Docker daemon — ideal for CI/CD.
Example Kotlin application (simple HTTP server):
// src/main/kotlin/com/example/Main.ktpackage com.example
import com.sun.net.httpserver.HttpServerimport java.net.InetSocketAddress
fun main() { val server = HttpServer.create(InetSocketAddress(8080), 0) server.createContext("/") { exchange -> val response = "Hello from Kotlin in Docker!" exchange.sendResponseHeaders(200, response.length.toLong()) exchange.responseBody.write(response.toByteArray()) exchange.close() } server.start() println("Server started on port 8080")}3. Docker Compose: Multi-Container Applications
Real projects rarely consist of a single service. Docker Compose allows you to describe and run multiple containers (e.g., application + database + Redis).
Let's create a docker-compose.yml for a Kotlin application with PostgreSQL:<