This episode prepares Kotlin applications for production: operational runbooks, monitoring and logging with error handling, deployment with containerization and CI/CD, and safe, fast release management and rollback.

An application that runs on your laptop is different from one operated in production. Episode 19 covers operational readiness: runbooks for daily operations, monitoring and logging, containerization and CI/CD, and release management with safe rollback.
Operational readiness isn't as exciting a topic as new features, but it's where reliability is determined. Good error handling, complete observability, and rollback-capable deployment are what separate a trusted application from one that's always "being fixed."
After this episode, you'll bring a Kotlin application to production with a clear plan.
A runbook is a step-by-step document for handling operational situations: how to restart a service, read logs, take a heap dump, or roll back a version. A good runbook reduces panic during incidents and keeps knowledge from living only in one person's head.
A Kotlin runbook typically covers the core commands:
./gradlew bootJar
docker logs --tail 200 aplikasi
curl http://localhost:8080/actuator/health./gradlew bootJar builds the artifact, docker logs checks the latest logs, and curl checks the health endpoint. These commands are documented in the runbook so anyone can execute them when needed.
For incidents, a runbook follows a clear structure: detection, diagnosis, mitigation, and resolution. Writing diagnosis steps before an incident happens — like "check GC metrics with jstat" or "check the error rate on the dashboard" — makes resolution far faster when it actually occurs.
Logging is the application's window in production. Kotlin uses SLF4J with implementations like Logback. Structured logs in JSON format are easier to search and aggregate:
import org.slf4j.LoggerFactory
private val log = LoggerFactory.getLogger("OrderService")
fun proses(id: String) {
log.info("Memproses order id={}", id)
try {
simpan(id)
} catch (e: Exception) {
log.error("Gagal memproses order id={}", id, e)
}
}The log.info("... id={}", id) pattern avoids wasteful string concatenation. Always include the exception in error logs so the stack trace is preserved. Good logging is the foundation of every diagnosis.
Monitoring measures health: CPU, heap memory, GC pauses, latency, and error rate. Health endpoints like Spring Actuator are exposed so orchestration can assess readiness:
curl -s http://localhost:8080/actuator/healthcurl -s http://localhost:8080/actuator/health returns the application's status. Metrics from actuator are exported to Prometheus, and Grafana displays them on dashboards. Alerting is built on top of metrics: without alerts, incidents are found by users.
Packaging a Kotlin application in a container makes deployment consistent across all environments. The Dockerfile uses a lightweight JRE image and separates dependency layers:
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY build/libs/aplikasi.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]-XX:MaxRAMPercentage=75.0 lets the JVM size its heap against the container's memory limit — an important pattern so the JVM doesn't exceed the limit. The image is built and run with standard Docker commands.
Automated CI/CD builds, tests, and deploys. A typical flow: a push to main triggers build and tests, then the Docker image is pushed to the registry, and the deployment runs:
jobs:
deploy:
steps:
- run: ./gradlew bootJar
- run: docker build -t app:$GITHUB_SHA .
- run: docker push registry.example/app:$GITHUB_SHAThe YAML block above builds, tags, and pushes the image with the commit SHA as the tag. SHA-based tags make every deployment identifiable and rollback-able to a specific version.
Safe deployments always have an exit path. Practices that make rollback fast:
A rollback is just a redeployment to a previous version, not a scary process when automated:
kubectl set image deployment/aplikasi app=registry.example/app:1.2.1The command kubectl set image rolls the image back to version 1.2.1. Because images are based on immutable tags and health checks are active, the deployment service waits for the app to be healthy before completing the rollback.
Episode 19 prepared Kotlin applications for production: runbooks for operations and incidents, structured logging and monitoring, containerization with Docker and a CI/CD pipeline, and release management with fast, safe rollback.
The key takeaways:
-XX:MaxRAMPercentage adapt the JVM to the memory limit.In episode 20 we'll discuss real-world use cases and patterns — examples of Android apps, backend services, and multiplatform libraries, clean, hexagonal, and reactive architecture, data flow and state management, and scalability and maintainability patterns for long-lived code.