docker-deployment

Docker containerization and deployment for Java/Spring Boot applications. Multi-stage builds, docker-compose, health checks, and CI/CD with GitHub Actions.

16 stars

Best use case

docker-deployment is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Docker containerization and deployment for Java/Spring Boot applications. Multi-stage builds, docker-compose, health checks, and CI/CD with GitHub Actions.

Teams using docker-deployment should expect a more consistent output, faster repeated execution, less prompt rewriting, better workflow continuity with your supporting tools.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.
  • You already have the supporting tools or dependencies needed by this skill.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/docker-deployment/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/devops/docker-deployment/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/docker-deployment/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How docker-deployment Compares

Feature / Agentdocker-deploymentStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Docker containerization and deployment for Java/Spring Boot applications. Multi-stage builds, docker-compose, health checks, and CI/CD with GitHub Actions.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Docker Deployment for Java/Spring Boot Applications

## When to Use This Skill

**Use when:**
- Containerizing a Java or Spring Boot application for consistent deployment
- Setting up local development environments with Docker Compose
- Building CI/CD pipelines that produce Docker images
- Deploying to container orchestration platforms (Kubernetes, ECS, Cloud Run)
- Ensuring reproducible builds across development, staging, and production
- Managing multi-service architectures locally (app + database + cache)

**Do NOT use when:**
- Building serverless functions (use cloud-native packaging instead)
- The application is a CLI tool or batch job that does not need containerization
- Deploying to a traditional VM-based environment with no container runtime
- The project is a library/SDK (publish to Maven Central, not a container registry)

---

## Multi-Stage Dockerfile for Java

Always use multi-stage builds to separate the build environment from the runtime. This produces smaller, more secure images.

### Standard Pattern

```dockerfile
# Stage 1: Build
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY pom.xml .
COPY .mvn/ .mvn/
COPY mvnw .
RUN chmod +x mvnw && ./mvnw dependency:go-offline -B
COPY src/ src/
RUN ./mvnw package -DskipTests -B

# Stage 2: Runtime
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /app/target/*.jar app.jar
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
```

### Key Principles

1. **Use JRE for runtime, not JDK** -- the JDK includes compilers and tools not needed at runtime
2. **Use Alpine-based images** when possible for smaller footprint (~200MB vs ~400MB)
3. **Run as non-root user** -- never run Java applications as root in containers
4. **Copy dependency resolution step first** to leverage Docker layer caching
5. **Use `.dockerignore`** to exclude unnecessary files from the build context

---

## JVM Options for Containers

Modern JVMs are container-aware (Java 10+), but explicit tuning is still recommended.

### Essential Flags

```bash
# Memory: use percentage-based limits so the JVM respects container memory limits
-XX:MaxRAMPercentage=75.0
-XX:InitialRAMPercentage=50.0
-XX:MinRAMPercentage=25.0

# GC selection
-XX:+UseG1GC                    # General-purpose, good for most workloads
-XX:+UseZGC                     # Low-latency, good for large heaps (Java 17+)
-XX:+UseShenandoahGC            # Alternative low-latency GC

# Container awareness (enabled by default in Java 10+, but explicit is safer)
-XX:+UseContainerSupport

# Diagnostics
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof
-XX:+ExitOnOutOfMemoryError
```

### Memory Sizing Guidelines

| Container Memory Limit | MaxRAMPercentage | Reasoning                                     |
|------------------------|------------------|------------------------------------------------|
| 256 MB                 | 75%              | Leaves ~64 MB for OS, native memory, threads   |
| 512 MB                 | 75%              | Leaves ~128 MB for overhead                    |
| 1 GB                   | 75%              | Standard for most microservices                 |
| 2 GB+                  | 80%              | Larger containers can use a higher percentage   |

### Common Pitfall: OOMKilled

If your container is killed by the OOM killer (exit code 137), the JVM heap is exceeding the container memory limit. Solutions:
- Lower `MaxRAMPercentage` (e.g., from 75% to 60%)
- Increase the container memory limit
- Check for native memory leaks (Netty buffers, JNI, thread stacks)
- Use `-XX:NativeMemoryTracking=summary` for diagnostics

---

## Docker Compose for Local Development

Use Docker Compose to run your application alongside its dependencies.

### Structure

```
project/
  docker-compose.yml          # Base configuration (production-like)
  docker-compose.dev.yml      # Dev overrides (debug, live reload)
  docker-compose.test.yml     # Test overrides (test DB, mocks)
  .env                        # Environment variables (not committed)
  .env.example                # Template for environment variables (committed)
```

### Override Pattern

```bash
# Development
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

# Testing
docker compose -f docker-compose.yml -f docker-compose.test.yml up

# Production (base only)
docker compose up -d
```

### Service Dependencies and Health Checks

Always use `depends_on` with `condition: service_healthy` so your application starts only after its dependencies are ready:

```yaml
services:
  app:
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
```

---

## Health Checks

### Spring Boot Actuator Health Endpoint

Add the Actuator dependency:

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```

Configure in `application.yml`:

```yaml
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  endpoint:
    health:
      show-details: when-authorized
      probes:
        enabled: true    # Enables /actuator/health/liveness and /readiness
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true
```

### Dockerfile Health Check

```dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD wget -qO- http://localhost:8080/actuator/health/liveness || exit 1
```

### Docker Compose Health Check

```yaml
services:
  app:
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health/liveness"]
      interval: 30s
      timeout: 3s
      start_period: 60s
      retries: 3
```

**Note:** Use `start_period` to give the JVM time to start. Spring Boot applications typically need 15-60 seconds to initialize.

---

## Environment Variables and Secrets

### Best Practices

1. **Never bake secrets into images** -- use environment variables or mounted secrets
2. **Use `.env` files for local development** -- never commit `.env` to version control
3. **Use Docker secrets or external secret managers in production** (Vault, AWS Secrets Manager, etc.)

### Spring Boot Environment Variable Binding

Spring Boot automatically maps environment variables to properties using relaxed binding:

```bash
# These are equivalent:
SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/myapp
spring.datasource.url=jdbc:postgresql://db:5432/myapp
```

### Docker Compose env_file

```yaml
services:
  app:
    env_file:
      - .env
    environment:
      # Override specific values or add non-secret config
      - SPRING_PROFILES_ACTIVE=docker
      - SERVER_PORT=8080
```

### .env.example Template

```bash
# Database
POSTGRES_DB=myapp
POSTGRES_USER=myapp
POSTGRES_PASSWORD=changeme

# Application
SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/myapp
SPRING_DATASOURCE_USERNAME=myapp
SPRING_DATASOURCE_PASSWORD=changeme

# Redis
SPRING_DATA_REDIS_HOST=redis
SPRING_DATA_REDIS_PORT=6379

# JWT / Security
JWT_SECRET=your-256-bit-secret-here
```

---

## .dockerignore

A proper `.dockerignore` reduces build context size and prevents sensitive files from being included:

```
# Build artifacts
target/
build/
*.jar
*.war
!target/*.jar

# IDE files
.idea/
*.iml
.vscode/
.project
.classpath
.settings/

# Version control
.git/
.gitignore

# Docker files (not needed in context)
docker-compose*.yml
Dockerfile*
.dockerignore

# Documentation
*.md
LICENSE
docs/

# Environment and secrets
.env
.env.*
!.env.example

# OS files
.DS_Store
Thumbs.db

# CI/CD
.github/
.gitlab-ci.yml
Jenkinsfile

# Test reports
surefire-reports/
failsafe-reports/
```

---

## Image Optimization

### Layer Caching Strategy

Order Dockerfile instructions from least to most frequently changing:

```dockerfile
# 1. Base image (rarely changes)
FROM eclipse-temurin:21-jre-alpine

# 2. System dependencies (rarely changes)
RUN apk add --no-cache wget

# 3. Application user (rarely changes)
RUN addgroup -S app && adduser -S app -G app

# 4. Dependencies (changes when pom.xml changes)
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./

# 5. Application code (changes most frequently)
COPY --from=builder /app/application/ ./
```

### Spring Boot Layered JAR

Spring Boot 2.3+ supports layered JARs that split the fat JAR into layers for better Docker caching:

```bash
# Enable layered JAR in pom.xml (Spring Boot 3.x -- enabled by default)
# Extract layers after building
java -Djarmode=layertools -jar app.jar extract --destination extracted

# Layers (from least to most frequently changing):
# 1. dependencies          - third-party libraries
# 2. spring-boot-loader    - Spring Boot loader classes
# 3. snapshot-dependencies  - SNAPSHOT dependencies
# 4. application           - your compiled code and resources
```

### Image Size Comparison

| Base Image                    | Approximate Size |
|-------------------------------|------------------|
| eclipse-temurin:21-jdk        | ~460 MB          |
| eclipse-temurin:21-jre        | ~280 MB          |
| eclipse-temurin:21-jre-alpine | ~180 MB          |
| Custom JRE (jlink)            | ~100 MB          |

### Custom JRE with jlink

For maximum optimization, create a custom JRE with only the modules your application needs:

```dockerfile
FROM eclipse-temurin:21-jdk-alpine AS jre-builder
RUN jlink \
    --add-modules java.base,java.logging,java.sql,java.naming,java.management,java.instrument,java.desktop,java.security.jgss,jdk.unsupported \
    --strip-debug \
    --no-man-pages \
    --no-header-files \
    --compress=zip-6 \
    --output /custom-jre
```

---

## GitHub Actions CI/CD

### Pipeline Structure

```
Push to main
  --> Run tests
  --> Build Docker image
  --> Push to container registry (GHCR)
  --> (optional) Deploy to staging
```

### Key Features

- **Layer caching** with `docker/build-push-action` and GitHub Actions cache
- **Multi-platform builds** with `docker/setup-buildx-action`
- **Semantic versioning** for image tags
- **Security scanning** with Trivy or Snyk

### Image Tagging Strategy

```yaml
tags: |
  ghcr.io/${{ github.repository }}:latest
  ghcr.io/${{ github.repository }}:${{ github.sha }}
  ghcr.io/${{ github.repository }}:${{ github.ref_name }}
```

For production, prefer immutable tags (`sha` or semver) over `latest`.

---

## Container Registries

### GitHub Container Registry (GHCR)

```bash
# Login
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin

# Tag and push
docker tag myapp:latest ghcr.io/OWNER/myapp:latest
docker push ghcr.io/OWNER/myapp:latest
```

### Docker Hub

```bash
docker login -u USERNAME
docker tag myapp:latest USERNAME/myapp:latest
docker push USERNAME/myapp:latest
```

### Amazon ECR

```bash
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin ACCOUNT.dkr.ecr.us-east-1.amazonaws.com
docker tag myapp:latest ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
docker push ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
```

---

## Spring Boot Docker Best Practices

### 1. Use Layered JARs

See `examples/Dockerfile.layered` for the complete example. Layered JARs dramatically improve rebuild times because dependency layers are cached.

### 2. Graceful Shutdown

```yaml
# application.yml
server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s
```

In the Dockerfile or entrypoint, ensure signals are forwarded to the JVM:

```dockerfile
# Use exec form (not shell form) so PID 1 is the JVM
ENTRYPOINT ["java", "-jar", "app.jar"]

# Or use tini as init process
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--", "java", "-jar", "app.jar"]
```

### 3. Spring Profiles for Docker

```yaml
# application-docker.yml
spring:
  datasource:
    url: jdbc:postgresql://${DB_HOST:postgres}:${DB_PORT:5432}/${DB_NAME:myapp}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  data:
    redis:
      host: ${REDIS_HOST:redis}
      port: ${REDIS_PORT:6379}

server:
  port: ${SERVER_PORT:8080}

logging:
  pattern:
    console: "%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n"
```

### 4. Log to stdout/stderr

Never log to files inside containers. Use console appenders and let the container runtime collect logs:

```yaml
logging:
  file:
    name:   # Explicitly empty -- no file logging
  level:
    root: INFO
    com.myapp: DEBUG
```

### 5. Externalize Configuration

Use environment variables or mounted config files. Never bake environment-specific configuration into the image.

---

## Production Deployment Considerations

### Resource Limits

Always set memory and CPU limits:

```yaml
# docker-compose.yml
services:
  app:
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: "1.0"
        reservations:
          memory: 512M
          cpus: "0.5"
```

### Logging

- Use JSON-formatted logs for structured log aggregation
- Ship logs to a centralized system (ELK, Loki, CloudWatch)
- Set appropriate log levels (INFO for production, DEBUG for troubleshooting)

### Security

- Scan images for vulnerabilities (`docker scout`, `trivy`, `snyk`)
- Use read-only file systems where possible (`read_only: true`)
- Drop all capabilities and add only what is needed
- Pin base image versions with digest hashes for reproducibility
- Never store secrets in image layers

### Networking

- Use Docker networks to isolate services
- Only expose ports that need to be publicly accessible
- Use internal DNS names for service-to-service communication

### Monitoring

- Expose Prometheus metrics via Actuator (`/actuator/prometheus`)
- Use liveness and readiness probes
- Monitor container resource usage (memory, CPU, restarts)
- Set up alerts for container OOM kills and restart loops

---

## Code Quality Checklist

Before deploying a containerized Java application, verify:

- [ ] **Multi-stage build**: Build and runtime stages are separate
- [ ] **Non-root user**: Application runs as a non-root user
- [ ] **Health check**: Dockerfile or Compose includes a health check
- [ ] **JVM tuning**: `MaxRAMPercentage` is set appropriately
- [ ] **Container memory limit**: `deploy.resources.limits.memory` is configured
- [ ] **No secrets in image**: All secrets come from environment variables or secret managers
- [ ] **.dockerignore**: Build context excludes unnecessary files
- [ ] **Graceful shutdown**: Spring Boot `server.shutdown=graceful` is enabled
- [ ] **Log to stdout**: No file-based logging inside the container
- [ ] **Image tagged**: Using immutable tags (SHA or semver), not just `latest`
- [ ] **Vulnerability scan**: Image has been scanned for known CVEs
- [ ] **Layered JAR**: Spring Boot layered JAR is used for optimal caching
- [ ] **Base image pinned**: Using a specific version tag, not `latest`
- [ ] **Entrypoint uses exec form**: Ensures proper signal handling
- [ ] **CI/CD pipeline**: Automated build, test, scan, and push workflow exists

Related Skills

gke-deployment

16
from diegosouzapw/awesome-omni-skill

Deploy, configure, and manage Kubernetes workloads on GKE with Deployments, Services, Ingress, HPA, health probes, ConfigMaps, and Secrets. Use when deploying containers to GKE, configuring load balancers, setting up autoscaling, writing health checks, managing environment configs, or troubleshooting pod issues.

frontend-deployment

16
from diegosouzapw/awesome-omni-skill

Deploy frontend applications from aramb.toml. Creates frontend service, resolves backend references from deployment outputs, builds static files, and deploys with environment variables. Returns deployment URL. Use for all frontend deployments.

featbit-deployment-kubernetes

16
from diegosouzapw/awesome-omni-skill

Deploys FeatBit to Kubernetes using Helm Charts. Use when user mentions "Kubernetes", "Helm", "K8s", "kubectl", works with values.yaml files, asks about "cloud deployment", "AKS", "EKS", "GKE", "ingress", or needs production-grade container orchestration setup.

featbit-deployment-docker

16
from diegosouzapw/awesome-omni-skill

Expert guidance for deploying FeatBit with Docker Compose across three tiers - Standalone (PostgreSQL only), Standard (PostgreSQL/MongoDB + Redis), and Professional (+ ClickHouse + Kafka). Use when user mentions "docker-compose", "deploy with Docker", "standalone vs standard vs pro", works with docker-compose.yml files, or asks about container configuration, environment variables, or production Docker setup.

FastAPI Kubernetes Deployment

16
from diegosouzapw/awesome-omni-skill

This skill should be used when the user asks to "deploy FastAPI to Kubernetes", "create Dockerfile", "build Docker image", "write Helm chart", "configure K8s deployment", "add health checks", "scale FastAPI", or mentions Docker, Kubernetes, K8s, containers, Helm, or deployment. Provides containerization and orchestration patterns.

expo-deployment

16
from diegosouzapw/awesome-omni-skill

Deploy Expo apps to production

dotnet-container-deployment

16
from diegosouzapw/awesome-omni-skill

Deploys .NET containers. Kubernetes probes, Docker Compose for local dev, CI/CD integration.

dockerfile-optimization

16
from diegosouzapw/awesome-omni-skill

Optimize Dockerfiles for smaller images, faster builds, better caching, and security. Use this skill when writing, reviewing, or debugging Dockerfiles.

Docker Hub Automation

16
from diegosouzapw/awesome-omni-skill

Automate Docker Hub tasks via Rube MCP (Composio): repositories, images, tags, and container registry management. Always search tools first for current schemas.

docker

16
from diegosouzapw/awesome-omni-skill

Docker y Compose. Proyecto usa este skill; contenido canónico en .ai-system.

docker-workflow

16
from diegosouzapw/awesome-omni-skill

Comprehensive Docker containerization workflow covering multi-stage builds, docker-compose orchestration, image optimization, debugging, and production best practices. Use when containerizing applications, setting up development environments, or deploying with Docker.

docker-vigil-orchestration

16
from diegosouzapw/awesome-omni-skill

Docker Compose orchestration for Vigil Guard v2.0.0 microservices (11 services). Use when deploying services, managing containers, troubleshooting Docker network issues, working with vigil-net, configuring docker-compose.yml, handling service dependencies, or working with 3-branch detection services (heuristics, semantic, prompt-guard).