Kotlin
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
175: Deploying Kotlin/Native Executables
By now, you know how to use Kotlin/Native to compile code into a standalone binary. It feels like magic—you hit build, and suddenly you have a file you can just drop onto a server without worrying about whether the user has a Java Runtime Environment installed. But if you've ever tried to actually distribute that binary to a colleague or a production server, you've likely run into the "GLIBC nightmare."
Let's say we've built a tool called LogSeal, a small CLI utility that encrypts local log files before they're uploaded to a cloud bucket. It's fast, it's native, and it works perfectly on your development machine. You run ./gradlew linkReleaseExecutableLinuxX64, grab the resulting binary, and SCP it over to a legacy Ubuntu server. You try to run it, and instead of encrypting logs, the OS screams: version 'GLIBC_2.34' not found.
The "It Works on My Machine" Trap
The naive way to deploy a Kotlin/Native executable is to build it on your current OS and assume that binary is portable across all machines of the same architecture. I did this early on, and it's a great way to waste an entire afternoon. The problem is that Kotlin/Native binaries are dynamically linked to the C library (glibc) of the host system where they were compiled.
If you build LogSeal on a modern machine running Ubuntu 22.04, your binary is linked against a newer version of glibc. When you move that binary to a server running Ubuntu 18.04 or CentOS 7, the binary looks for symbols and versions of the C library that simply don't exist on that older kernel. It doesn't matter that both are "Linux x64"—the underlying system libraries are mismatched. You're essentially shipping a piece of a puzzle that only fits into your specific OS version.
Standardizing the Build Environment
The professional way to handle this isn't to try and find a "universal" compiler, but to control the environment where the compilation happens. To ensure LogSeal runs on as many Linux distributions as possible, you need to build it on the oldest version of the target OS you intend to support.
I usually handle this by moving the build process into a Docker container. Instead of running the Gradle task on my MacBook or my primary Linux workstation, I trigger the build inside a container based on an older, stable image—like ubuntu:20.04. Because the binary is linked against the older glibc present in that container, it will remain compatible with any system that has that version or newer. Since almost every modern Linux server is at least as new as Ubuntu 20.04, your binary suddenly becomes truly portable.
There is a trade-off here, of course. Building in a container adds a layer of complexity to your CI/CD pipeline. You can't just run a local Gradle command; you have to manage volume mounts for your source code and ensure the container has the necessary Kotlin Native toolchains installed. But compared to the alternative—spending hours debugging segmentation faults on a production server—it's a price I'm always willing to pay.
// In a real-world CI config, your build step would look something like this:
docker run --rm -v $(pwd):/home/kotlin/project -w /home/kotlin/project \
kotlinlang/kotlin-native-compiler:1.9.0 \
./gradlew linkReleaseExecutableLinuxX64
One last tip: if you absolutely must avoid the glibc headache entirely, you can look into static linking. However, in the Kotlin/Native ecosystem, this is significantly more complex and often requires wrestling with the linker flags to bundle everything into the binary. For 95% of use cases, the "build on the oldest target" strategy via Docker is the path of least resistance.
📋 Practical Task
Building a Portable LogSeal Binary via Docker
You have the LogSeal source code locally, but you need to ensure the resulting executable runs on a legacy Debian server that uses an older glibc version.
- Create a
Dockerfilethat uses a stable, older Linux base image (e.g.,ubuntu:20.04). - Install the necessary build tools (openjdk and any required native dependencies) within that image.
- Configure a command that mounts your current project directory into the container and executes the
linkReleaseExecutableLinuxX64Gradle task. - Verify that the resulting binary in
build/bin/linuxX64/releaseExecutable/is the one generated by the container and not your local machine.
There are no comments for now.