WarpBuild LogoWarpBuild Docs

Kotlin with Gradle

Best practices for Dockerfile for Kotlin with Gradle

🐳 Annotated Dockerfile for Kotlin with Gradle:

# Stage 1: Build the application
FROM eclipse-temurin:21 AS builder
 
# Set working directory
WORKDIR /app
 
# Copy Gradle configuration files
COPY gradle/ gradle/
COPY gradlew gradlew.bat settings.gradle.kts build.gradle.kts ./
 
# Use Gradle cache mount for faster builds
RUN --mount=type=cache,target=/root/.gradle \
    ./gradlew dependencies --no-daemon
 
# Copy source code
COPY src/ ./src/
 
# Build the application
RUN --mount=type=cache,target=/root/.gradle \
    ./gradlew build --no-daemon -x test
 
# Stage 2: Create minimal runtime image
FROM eclipse-temurin:21-alpine
 
# Create a non-root user
RUN groupadd -r kotlinuser && useradd -r -g kotlinuser kotlinuser
 
# Set working directory
WORKDIR /app
 
# Copy the built artifacts from the builder stage
COPY --from=builder /app/build/libs/*.jar app.jar
 
# Set ownership and permissions
RUN chown -R kotlinuser:kotlinuser /app
 
# Switch to non-root user
USER kotlinuser
 
# Expose application port
EXPOSE 8080
 
# Configure JVM options for containerized environments
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom"
 
# Run the application
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -jar app.jar" ]

🔍 Why these are best practices:

✅ Multi-stage builds

  • Reduces the final image size by separating build environment from runtime.
  • Eliminates build tools and intermediate files from the production image.
  • Creates a smaller, more secure runtime environment.

✅ Gradle dependency caching

  • Uses Docker's build cache to avoid downloading dependencies repeatedly.
  • Dramatically speeds up iterative builds in development and CI/CD.
  • Leverages --mount=type=cache for efficient dependency management.

✅ JRE-only runtime image

  • Uses a smaller JRE image instead of full JDK for the final runtime.
  • Reduces attack surface by removing development tools from production.
  • Decreases image size, improving deployment speed and resource usage.

✅ Container-aware JVM settings

  • Configures the JVM to respect container memory limits.
  • Optimizes garbage collection and memory usage for containerized environments.
  • Improves application stability and resource utilization.

✅ Security best practices

  • Runs the application as a non-root user to enhance container security.
  • Follows the principle of least privilege to limit potential attack vectors.
  • Sets proper file ownership and permissions.

🚀 Additional Dockerfile best practices you can adopt:

Optimize Kotlin applications for native compilation

For Kotlin/Native applications or using GraalVM:

# For GraalVM native image compilation
FROM ghcr.io/graalvm/native-image-community:21 AS builder
WORKDIR /app
COPY . /app
RUN ./gradlew nativeCompile
 
# Minimal runtime image
FROM gcr.io/distroless/base
COPY --from=builder /app/build/native/nativeCompile/app /app
ENTRYPOINT ["/app"]

Enable Spring Boot layered JARs

For Spring Boot applications to improve caching:

# Extract layered JAR
COPY --from=builder /app/build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
 
# Create optimized layers
FROM eclipse-temurin:21-alpine
WORKDIR /app
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
 
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

Add health checks

Monitor container health for better orchestration:

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

Use .dockerignore

Exclude unnecessary files from your Docker build context:

.gradle/
build/
!build/libs/*.jar
.git/
.github/
.gitignore
*.md
.idea/
*.iml

Environment-specific configurations

Configure for different environments:

# Use build arguments to customize the build
ARG PROFILE=production
RUN ./gradlew build -Pprofile=${PROFILE} --no-daemon -x test

JVM performance tuning for containers

Fine-tune JVM settings:

ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:InitialRAMPercentage=50.0 -XX:+UseG1GC -XX:+AlwaysPreTouch -XX:+ExitOnOutOfMemoryError"

Implement proper signaling

Ensure your application responds to container orchestration signals:

# For Springboot apps
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Dserver.shutdown=graceful"
 
# Or add a lightweight init system
RUN apt-get update && apt-get install -y --no-install-recommends dumb-init
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["java", "-jar", "app.jar"]

By following these practices, you'll create Docker images for your Kotlin applications that are secure, efficient, and optimized for both development and production environments. Kotlin's JVM foundation combined with these Docker techniques provides excellent performance while maintaining developer productivity.

Last updated on