Containerizing Kotlin Applications with Docker: A Complete Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Containerizing Kotlin is a Must-Have for Modern Developers?

\n\n

Imagine this scenario: you've written a cool Kotlin application on your MacBook. Everything works perfectly. You send the code to a colleague on Windows — the database won't connect. To another on Linux — the JDK version is wrong. Sound familiar? Docker solves this problem once and for all.

\n\n

Containerizing Kotlin applications is not just a trendy fad. It's a way to guarantee that your application will run identically on any machine: from a junior's laptop to a production server on AWS. In this article, we'll break down how to properly package a Kotlin application into a Docker image, optimize its size, and configure it for production.

\n\n

What you will learn:

\n
    \n
  • How to write a Dockerfile for a Kotlin application with Gradle
  • \n
  • How to reduce the image size by 3-5 times
  • \n
  • How to use Docker Compose for a Kotlin + database stack
  • \n
  • Best practices for production containerization
  • \n
\n\n

Basic Dockerfile for a Kotlin Application

\n\n

Let's start simple. Suppose you have a standard Kotlin application built with Gradle. The first thing that comes to mind is to take a JDK image, copy the source code, and run the build. Let's do that, but immediately incorporating best practices.

\n\n

Monolithic build (don't do this in production)

\n\n
# Dockerfile\nFROM gradle:7.6-jdk17 AS build\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\nFROM openjdk:17-jdk-slim\nWORKDIR /app\nCOPY --from=build /app/build/libs/app.jar app.jar\nEXPOSE 8080\nCMD ["java", "-jar", "app.jar"]
\n\n

This approach works, but it has three serious drawbacks:

\n
    \n
  • Size: the image weighs about 400-500 MB due to the full JDK
  • \n
  • Caching: with any change to the source code, Gradle will re-download dependencies
  • \n
  • Security: build tools that are not needed at runtime remain in the image
  • \n
\n\n

Optimized Build: Multi-stage and Caching

\n\n

The real magic of Docker is multi-stage builds. We will split the process into three stages:

\n
    \n
  • Download dependencies (cached)
  • \n
  • Build the application
  • \n
  • Final minimal image
  • \n
\n\n

Proper Dockerfile for Kotlin + Gradle

\n\n
# Stage 1: Download dependencies\nFROM gradle:7.6-jdk17 AS dependencies\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN gradle dependencies --no-daemon\n\n# Stage 2: Build\nFROM dependencies AS build\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\n# Stage 3: Final image\nFROM eclipse-temurin:17-jre-alpine\nWORKDIR /app\nCOPY --from=build /app/build/libs/app.jar app.jar\nRUN addgroup -S appgroup && adduser -S appuser -G appgroup\nUSER appuser\nEXPOSE 8080\nCMD ["java", "-jar", "app.jar"]
\n\n

What changed?

\n
    \n
  • Alpine JRE instead of the full JDK — the image shrinks to ~120 MB
  • \n
  • Dependency caching — if build.gradle.kts hasn't changed, Docker uses the cache
  • \n
  • Security — the application runs as an unprivileged user
  • \n
\n\n

Building with Gradle Wrapper (recommended approach)

\n\n

If you use the Gradle Wrapper (and you should!), the Dockerfile becomes even more elegant. The Wrapper ensures that the Gradle version is the same for all developers and in CI/CD.

\n\n
FROM eclipse-temurin:17-jdk-alpine AS builder\nWORKDIR /app\nCOPY gradlew gradlew.bat ./\nCOPY gradle ./gradle\nRUN ./gradlew --version\n\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN ./gradlew dependencies --no-daemon\n\nCOPY src ./src\nRUN ./gradlew build --no-daemon -x test\n\nFROM eclipse-temurin:17-jre-alpine\nWORKDIR /app\nCOPY --from=builder                

Blogs

Book Recommendations