Skip to content

Docker Image Optimization Guide

Doc Version: 1.1.0 Last Updated: 2026-04-16 Git Commit: f58f0e0 Author: Lincoln

Overview

JAiRouter provides an optimized Docker image build solution. Through multi-stage builds and Alpine base images, the image size is reduced from 440MB to 281MB (36% reduction).

Note: The JLink solution attempts to use jlink tool for custom JRE modules, but due to Spring Boot 3.x module compatibility issues, it ultimately uses Alpine JRE + multi-stage build + JVM parameter optimization.

The JLink image (Dockerfile.jlink) has the same size as the optimized version (281MB) and is provided as an experimental option for users with special requirements.

Image Comparison

Image TypeDockerfileSizeBase ImageBuild MethodRecommendation
OptimizedDockerfile.optimized281MBeclipse-temurin:17-jre-alpineMulti-stage + layertools⭐⭐⭐⭐⭐
JLinkDockerfile.jlink281MBeclipse-temurin:17-jre-alpineMulti-stage + JVM optimization🔬Experimental
StandardDockerfile440MBeclipse-temurin:17-jreSingle-stage⭐⭐⭐

Optimization Techniques

1. Alpine Base Image

Uses eclipse-temurin:17-jre-alpine instead of standard JRE image:

# Optimized version
FROM eclipse-temurin:17-jre-alpine

# Standard version
FROM eclipse-temurin:17-jre

Advantages: - Alpine image is only 5MB, while Debian base image is about 120MB - Smaller attack surface, better security - Faster pull speed

2. Multi-stage Build

Uses two build stages to separate build and runtime environments:

# Stage 1: Build stage
FROM eclipse-temurin:17-jre-alpine AS extract-layer
COPY target/model-router-*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract --destination extracted

# Stage 2: Runtime stage
FROM eclipse-temurin:17-jre-alpine
COPY --from=extract-layer /app/extracted/dependencies/ ./
COPY --from=extract-layer /app/extracted/spring-boot-loader/ ./
COPY --from=extract-layer /app/extracted/snapshot-dependencies/ ./
COPY --from=extract-layer /app/extracted/application/ ./

Advantages: - Final image doesn't contain build tools - Fewer security vulnerabilities - Smaller image layers

3. Spring Boot Layertools

Uses Spring Boot's built-in layertools feature to extract JAR layers:

java -Djarmode=layertools -jar app.jar extract --destination extracted

Extracted Layers: 1. dependencies/ - Third-party dependencies 2. spring-boot-loader/ - Spring Boot loader 3. snapshot-dependencies/ - SNAPSHOT dependencies 4. application/ - Application code

Advantages: - Leverages Docker caching mechanism - Only rebuilds relevant layers when dependencies change - Faster build times

4. JVM Parameter Optimization

JVM parameters optimized for container environments:

# Optimized/JLink version
-Xms128m -Xmx256m -XX:+UseG1GC -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0

# Standard version
-Xms512m -Xmx1024m -XX:+UseG1GC -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0

Optimization Points: - Smaller initial heap memory (128MB vs 512MB) - Smaller max heap memory (256MB vs 1024MB) - Container awareness enabled (UseContainerSupport) - Percentage-based memory limit (MaxRAMPercentage)

Attempts to use jlink tool to create custom JRE:

# Stage 2: Create jlink custom JRE
FROM eclipse-temurin:17-jdk-alpine AS jlink-create

RUN /opt/java/openjdk/bin/jlink \
    --add-modules ALL-MODULE-PATH \
    --compress=2 \
    --no-header-files \
    --no-man-pages \
    --strip-debug \
    --output /jlink-runtime

Technical Notes: - ⚠️ Spring Boot 3.x uses module system, requires complete JRE modules - ⚠️ Attempting to strip modules causes compatibility issues like ClassNotFoundException - ✅ Final solution: Alpine JRE + multi-stage build + JVM parameter optimization - 📊 Image size: 281MB (same as optimized version)

JLink Version Use Cases: - Advanced users who need to further customize JRE modules - Scenarios willing to take compatibility risks for potential optimization - Technical reference and experimental purposes

Building Optimized Image

# Build optimized image (recommended)
./scripts/build/docker-build.sh optimized

# Build JLink image (experimental)
./scripts/build/docker-build.sh jlink

# Build standard image
./scripts/build/docker-build.sh standard

Manual Build

# Build JAR
mvn clean package -DskipTests -Pfast

# Build optimized image
docker build -f Dockerfile.optimized \
    -t sodlinken/jairouter:latest-optimized \
    .

Running Optimized Image

Basic Run

docker run -d \
  --name jairouter \
  -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e JAIROUTER_SECURITY_ENABLED=true \
  -e JAIROUTER_SECURITY_JWT_ENABLED=true \
  -e JAIROUTER_SECURITY_JWT_SECRET="your-32-char-secret-key-here" \
  sodlinken/jairouter:latest-optimized

Production Environment Run

docker run -d \
  --name jairouter \
  -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e JAIROUTER_SECURITY_ENABLED=true \
  -e JAIROUTER_SECURITY_JWT_ENABLED=true \
  -e JAIROUTER_SECURITY_JWT_SECRET="your-very-strong-jwt-secret-key-at-least-32-characters-long" \
  -e JAIROUTER_SECURITY_API_KEY_ENABLED=true \
  -v $(pwd)/config:/app/config:ro \
  -v $(pwd)/logs:/app/logs \
  -v $(pwd)/data:/app/data \
  --restart unless-stopped \
  sodlinken/jairouter:latest-optimized

Using Run Script

# Run optimized version (recommended)
./scripts/build/docker-run.sh prod latest optimized

# Run standard version
./scripts/build/docker-run.sh prod latest standard

Performance Comparison

Image Size

# Check image size
docker images sodlinken/jairouter

# Example output
REPOSITORY            TAG               SIZE
sodlinken/jairouter   latest-optimized  281MB
sodlinken/jairouter   latest-jlink      281MB
sodlinken/jairouter   latest            440MB

Startup Time

Image TypeCold StartupHot Startup
Optimized~8-10 seconds~3-5 seconds
Standard~10-12 seconds~4-6 seconds

Resource Usage

Image TypeInitial MemoryRunning MemoryCPU Usage
Optimized~200MB~350MBLow
Standard~250MB~400MBLow

Security

Non-root User

Optimized image runs as non-root user:

RUN addgroup -g 10010 -S jairouter && \
    adduser -u 10010 -S jairouter -G jairouter

USER jairouter

Minimal Privileges

# Set only necessary permissions
RUN chmod -R 755 /app

Security Scanning

Recommended to use Docker Scout or Trivy for image scanning:

# Using Docker Scout
docker scout cve sodlinken/jairouter:latest-optimized

# Using Trivy
trivy image sodlinken/jairouter:latest-optimized

Best Practices

1. Use Optimized Image

Recommended to use optimized image in production:

# docker-compose.yml
services:
  jairouter:
    image: sodlinken/jairouter:latest-optimized
    container_name: jairouter

2. Image Tag Management

# Use specific version tags
docker pull sodlinken/jairouter:v1.7.0-optimized

# Avoid using latest tag in production
# docker pull sodlinken/jairouter:latest-optimized  # Not recommended for production

3. Image Update Strategy

# Update base image regularly
# Update base image version in Dockerfile.optimized
FROM eclipse-temurin:17-jre-alpine:latest

4. Monitor Image Size

# Use docker history to check image layers
docker history sodlinken/jairouter:latest-optimized

# Use dive tool to analyze image layers
dive sodlinken/jairouter:latest-optimized

Troubleshooting

Image Build Failure

# Check if JAR file exists
ls -la target/model-router-*.jar

# Clean build cache
docker builder prune -a

# Rebuild without cache
docker build --no-cache -f Dockerfile.optimized -t sodlinken/jairouter:latest-optimized .

Container Startup Failure

# Check container logs
docker logs jairouter

# Enter container for debugging
docker exec -it jairouter sh

# Check JVM parameters
docker exec jairouter java -XX:+PrintFlagsFinal -version

Slow Image Pull

# Use domestic mirror accelerator
# Configure /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://registry.cn-hangzhou.aliyuncs.com"
  ]
}

Next Steps