---
title: "Kotlin com Docker: Guia Completo em Português | Kotlin Brasil"
url: "https://kotlin.dev.br/guias/guia-kotlin-docker/"
markdown_url: "https://kotlin.dev.br/guias/guia-kotlin-docker.MD"
description: "Aprenda a containerizar aplicações Kotlin com Docker. Guia completo com Dockerfile, multi-stage builds, Docker Compose e boas práticas."
date: "2025-07-25"
author: "Karina Melo"
---

# Kotlin com Docker: Guia Completo em Português | Kotlin Brasil

Aprenda a containerizar aplicações Kotlin com Docker. Guia completo com Dockerfile, multi-stage builds, Docker Compose e boas práticas.


Docker revolucionou a forma como empacotamos e distribuimos aplicações, e projetos Kotlin se beneficiam enormemente dessa tecnologia. Containerizar uma aplicação Kotlin garante que ela rode de forma identica em qualquer ambiente, eliminando o clássico problema do "funciona na minha maquina". Neste guia, vamos desde a criação de Dockerfiles otimizados até orquestracao com Docker Compose, cobrindo aplicações Spring Boot, Ktor e scripts Kotlin puros. Você vai aprender a construir imagens leves, seguras e prontas para producao.

## Por Que Docker para Projetos Kotlin

Aplicações Kotlin rodam na JVM, que precisa estar instalada e configurada corretamente no servidor. Docker encapsula a JVM, as dependências é a aplicação em um container isolado. As vantagens incluem ambiente consistente entre desenvolvimento e producao, facilidade de escalar horizontalmente, isolamento de processos e deploy simplificado.

## Dockerfile Básico para Kotlin

Vamos começar com um Dockerfile simples para uma aplicação Kotlin com Gradle:

```kotlin
// Dockerfile
FROM eclipse-temurin:17-jdk AS build
WORKDIR /app

# Copiar arquivos de configuracao do Gradle primeiro (para cache de dependências)
COPY gradle gradle
COPY gradlew .
COPY build.gradle.kts .
COPY settings.gradle.kts .
COPY gradle.properties .

# Baixar dependências (camada cacheada)
RUN chmod +x gradlew && ./gradlew dependencies --no-daemon

# Copiar codigo fonte
COPY src src

# Compilar
RUN ./gradlew build -x test --no-daemon

# Imagem de runtime
FROM eclipse-temurin:17-jre
WORKDIR /app

COPY --from=build /app/build/libs/*-all.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]
```

## Multi-Stage Build Otimizado

O multi-stage build separa o ambiente de compilação do runtime, resultando em imagens significativamente menores:

```kotlin
// Dockerfile otimizado
# Etapa 1: Cache de dependências
FROM eclipse-temurin:17-jdk-alpine AS deps
WORKDIR /app
COPY gradle gradle
COPY gradlew .
COPY build.gradle.kts .
COPY settings.gradle.kts .
RUN chmod +x gradlew && ./gradlew dependencies --no-daemon

# Etapa 2: Build
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /app
COPY --from=deps /root/.gradle /root/.gradle
COPY . .
RUN chmod +x gradlew && ./gradlew shadowJar --no-daemon

# Etapa 3: Runtime
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# Criar usuario nao-root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Copiar apenas o JAR
COPY --from=build /app/build/libs/*-all.jar app.jar

# Configuração de segurança
USER appuser

# Configuração da JVM
ENV JAVA_OPTS="-XX:+UseContainerSupport \
    -XX:MaxRAMPercentage=75.0 \
    -XX:+UseG1GC \
    -Djava.security.egd=file:/dev/./urandom"

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
    CMD wget -qO- http://localhost:8080/health || exit 1

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
```

A imagem final usa `jre-alpine` em vez de `jdk`, reduzindo o tamanho de centenas de MB para dezenas de MB.

## Dockerfile para Spring Boot

O Spring Boot oferece suporte nativo a build de imagens via Buildpacks, mas Dockerfiles manuais oferecem mais controle:

```kotlin
// Dockerfile para Spring Boot com layers
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN chmod +x gradlew && ./gradlew bootJar --no-daemon

# Extrair layers do Spring Boot
FROM eclipse-temurin:17-jdk-alpine AS layers
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract

# Runtime com layers separadas (melhor cache do Docker)
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

RUN addgroup -S spring && adduser -S spring -G spring
USER spring

COPY --from=layers /app/dependencies/ ./
COPY --from=layers /app/spring-boot-loader/ ./
COPY --from=layers /app/snapshot-dependencies/ ./
COPY --from=layers /app/application/ ./

EXPOSE 8080

ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
```

As layers do Spring Boot permitem que o Docker reutilize cache para dependências que não mudaram, acelerando builds subsequentes.

## Dockerfile para Ktor

```kotlin
// Dockerfile para Ktor
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN chmod +x gradlew && ./gradlew buildFatJar --no-daemon

FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

RUN addgroup -S ktor && adduser -S ktor -G ktor
USER ktor

COPY --from=build /app/build/libs/*-all.jar app.jar

ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"

EXPOSE 8080

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
```

## Docker Compose para Desenvolvimento

O Docker Compose orquestra múltiplos containers para desenvolvimento local:

```kotlin
// docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=jdbc:postgresql://postgres:5432/meuapp
      - DATABASE_USER=appuser
      - DATABASE_PASSWORD=apppass
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - app-network

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: meuapp
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: apppass
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d meuapp"]
      interval: 5s
      timeout: 3s
      retries: 5
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - app-network

  pgadmin:
    image: dpage/pgadmin4
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@local.dev
      PGADMIN_DEFAULT_PASSWORD: admin
    ports:
      - "5050:80"
    depends_on:
      - postgres
    networks:
      - app-network

volumes:
  postgres-data:
  redis-data:

networks:
  app-network:
    driver: bridge
```

## Docker Compose para Testes de Integração

Crie um compose separado para testes:

```kotlin
// docker-compose.test.yml
version: '3.8'

services:
  test-db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: testdb
      POSTGRES_USER: testuser
      POSTGRES_PASSWORD: testpass
    ports:
      - "5433:5432"
    tmpfs:
      - /var/lib/postgresql/data  # Mais rapido para testes
```

No Gradle, crie uma task que gerencia o ciclo de vida dos containers de teste:

```kotlin
// build.gradle.kts
tasks.register<Exec>("startTestContainers") {
    commandLine("docker-compose", "-f", "docker-compose.test.yml", "up", "-d")
}

tasks.register<Exec>("stopTestContainers") {
    commandLine("docker-compose", "-f", "docker-compose.test.yml", "down")
}
```

## Configuração da JVM para Containers

A JVM precisa de configurações específicas para funcionar bem em containers:

```kotlin
// application.conf (Ktor) ou application.yml (Spring)
// A JVM deve respeitar os limites de memoria do container

// Flags importantes:
// -XX:+UseContainerSupport  -> JVM respeita cgroups
// -XX:MaxRAMPercentage=75.0 -> Usa 75% da RAM do container
// -XX:+UseG1GC              -> GC recomendado para containers
// -XX:+ExitOnOutOfMemoryError -> Reinicia o container em OOM
```

No Kotlin, configure o health check endpoint:

```kotlin
// Health check para Docker
fun Application.configurarHealthCheck() {
    routing {
        get("/health") {
            // Verificar dependências
            val dbOk = verificarBancoDeDados()
            val redisOk = verificarRedis()

            if (dbOk && redisOk) {
                call.respond(HttpStatusCode.OK, mapOf(
                    "status" to "UP",
                    "database" to "OK",
                    "redis" to "OK"
                ))
            } else {
                call.respond(HttpStatusCode.ServiceUnavailable, mapOf(
                    "status" to "DOWN",
                    "database" to if (dbOk) "OK" else "FAIL",
                    "redis" to if (redisOk) "OK" else "FAIL"
                ))
            }
        }
    }
}
```

## Boas Práticas para Kotlin com Docker

- **Use multi-stage builds**: separe compilação de runtime para imagens menores e mais seguras.
- **Imagens Alpine**: prefira imagens baseadas em Alpine Linux para reduzir tamanho.
- **Usuario não-root**: nunca rode a aplicação como root no container.
- **Ordene COPY para cache**: copie arquivos que mudam menos (gradle, build configs) antes do código fonte.
- **Use .dockerignore**: exclua `.git`, `build/`, `.gradle/`, `*.md` e outros arquivos desnecessários.
- **Configure health checks**: permita que orquestradores detectem e reiniciem containers com problemas.
- **Limite recursos**: defina limites de CPU e memória no Docker Compose ou Kubernetes.
- **Use variaveis de ambiente para configuração**: nunca hardcode URLs, credenciais ou parametros de ambiente na imagem.

## Erros Comuns e Armadilhas

- **Imagem grande demais**: usar `jdk` em vez de `jre` no runtime ou não usar Alpine dobra ou triplica o tamanho da imagem.
- **Cache de build não aproveitado**: copiar todo o projeto antes de baixar dependências invalida o cache a cada mudanca de código.
- **JVM ignorando limites do container**: sem `-XX:+UseContainerSupport`, a JVM pode alocar mais memória do que o container permite, causando OOM killer.
- **Rodar como root**: vulnerabilidades na aplicação podem comprometer o host se o container roda como root.
- **Secrets em variaveis de ambiente visíveis**: use Docker secrets ou ferramentas como HashiCorp Vault para credenciais sensiveis.
- **Nao configurar graceful shutdown**: a aplicação deve responder ao sinal SIGTERM para encerrar conexoes e processos pendentes antes de parar.

## Conclusão e Próximos Passos

Docker e uma ferramenta indispensavel para projetos Kotlin modernos, desde desenvolvimento local até deploy em producao. Com multi-stage builds, configuração adequada da JVM e Docker Compose, você tem um ambiente reproduzível e eficiente. Para ir além, explore Kubernetes para orquestracao de containers em escala, consulte nosso guia de CI/CD para integrar Docker ao pipeline de deploy e estude microsserviços para arquiteturas distribuídas containerizadas. Docker e Kubernetes são escritos em <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go</a>, e <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a> produz imagens Docker extremamente pequenas (sem runtime) — vale conhecer ambas para entender o ecossistema de containers.
