CODE WITH SIBIN

Solving Real Problems with Real Code


Skip the Dockerfile: How to Containerize Spring Boot Like a Pro

Introduction

Containerizing Spring Boot applications improves deployment consistency, scalability, and security. While traditional Dockerfile-based approaches work, newer methods eliminate manual configuration, reduce errors, and leverage optimized build processes.

This guide explores four Dockerfile-free approaches to containerize Spring Boot apps, comparing their benefits, configurations, and best practices.

1. Why Dockerize Without a Dockerfile?

1.1 Simplification & Automation

  • No need to manually write Dockerfile instructions.
  • Automatic dependency resolution and layer optimization.
  • Built-in best practices for security and performance.

1.2 Security & Maintenance

  • Base images are automatically updated (e.g., JVM patches).
  • Reduced risk of misconfigurations in Dockerfile.
  • Smaller attack surface due to optimized layering.

1.3 Portability & CI/CD Integration

  • Works seamlessly across different environments (local, cloud, Kubernetes).
  • Faster builds with caching and incremental updates.
  • Native integration with Maven/Gradle and cloud platforms.

2. Approach 1: Using Spring Boot’s Built-in Build Plugin

2.1 How It Works

Spring Boot 2.3+ integrates with Cloud Native Buildpacks to generate OCI-compliant images without a Dockerfile.

2.2 Maven Configuration

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <image>
                    <name>myapp:${project.version}</name>
                    <builder>paketobuildpacks/builder:base</builder>
                    <env>
                        <BP_JVM_VERSION>17</BP_JVM_VERSION>
                    </env>
                </image>
            </configuration>
        </plugin>
    </plugins>
</build>

2.3 Gradle Configuration

plugins {
    id 'org.springframework.boot' version '3.1.0'
}

bootBuildImage {
    imageName = "myapp:${project.version}"
    builder = "paketobuildpacks/builder:base"
    environment = [
        "BP_JVM_VERSION": "17",
        "BPE_APPEND_JAVA_TOOL_OPTIONS": "-Dspring.profiles.active=prod"
    ]
}

2.4 Build Command

# Maven
./mvnw spring-boot:build-image

# Gradle
./gradlew bootBuildImage

2.5 Advantages

✅ Zero configuration for most apps.
✅ Automatic security updates via Paketo Buildpacks.
✅ Supports ARM64 & AMD64 out of the box.

3. Approach 2: Using Google’s Jib Plugin

3.1 How Jib Works

  • Builds optimized Docker images without Docker daemon.
  • Creates distroless images (smaller & more secure).

3.2 Maven Setup

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>3.3.1</version>
    <configuration>
        <from>
            <image>eclipse-temurin:17-jre-jammy</image>
        </from>
        <to>
            <image>myapp:${project.version}</image>
        </to>
        <container>
            <ports>
                <port>8080</port>
            </ports>
            <jvmFlags>
                <jvmFlag>-Dspring.profiles.active=prod</jvmFlag>
            </jvmFlags>
        </container>
    </configuration>
</plugin>

3.3 Gradle Setup

plugins {
    id 'com.google.cloud.tools.jib' version '3.3.1'
}

jib {
    from {
        image = 'eclipse-temurin:17-jre-jammy'
    }
    to {
        image = "myapp:${project.version}"
    }
    container {
        ports = ['8080']
        jvmFlags = ['-Dspring.profiles.active=prod']
    }
}

3.4 Build Commands

# Build & push to registry
./mvnw jib:build

# Build to local Docker daemon
./mvnw jib:dockerBuild

3.5 Advantages

✅ No Docker daemon needed (faster CI/CD).
✅ Smallest possible image size (~100MB).
✅ Layer caching for faster builds.

4. Approach 3: Cloud Native Buildpacks (Pack CLI)

4.1 What Are Buildpacks?

  • Automatically detect frameworks (Java, Node.js, etc.).
  • Apply optimizations (memory, security, JVM tuning).

4.2 Using pack CLI

1. Install:

brew install buildpacks/tap/pack

2. Build:

pack build myapp --builder paketobuildpacks/builder:base \
    --env BP_JVM_VERSION=17 \
    --env BPE_DELIM_JAVA_TOOL_OPTIONS=" " \
    --env BPE_APPEND_JAVA_TOOL_OPTIONS="-Dspring.profiles.active=prod"

4.3 Advantages

✅ Works with any language (not just Java).
✅ Enterprise-ready (used in Heroku, Cloud Foundry).
✅ Automatic security patches.

5. Approach 4: Docker Compose with Auto-Generated Image

5.1 Using Spring Boot + Docker Compose

1. Generate image first:

./gradlew bootBuildImage

2. Define docker-compose.yml:

version: '3.8'
services:
  app:
    image: myapp:latest
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod

5.2 Advantages

✅ Easy local development.
✅ Multi-container setups (DB, Redis, etc.).

6. Comparison: Which Method Should You Use?

MethodBest ForImage SizeBuild SpeedCustomization
Spring Boot PluginSimple deploymentsMedium (~200MB)FastLow
JibCI/CD, security-sensitiveSmall (~100MB)Very FastMedium
Buildpacks (Pack CLI)Polyglot apps, cloud-nativeMedium (~200MB)ModerateMedium
Docker ComposeLocal developmentDependsFastLow

7. Best Practices for Production

7.1 Image Tagging

Always use versioned tags:

./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=myapp:1.0.0

7.2 Minimize Image Size

  • Use Jib’s distroless base images.
  • With Buildpacks, use builder:base instead of builder:full.

7.3 Security Scanning

docker scan myapp:latest

7.4 Environment-Specific Configs

bootBuildImage {
    environment = [
        "BP_JVM_VERSION": "17",
        "BPE_APPEND_JAVA_TOOL_OPTIONS": "-Dspring.profiles.active=${System.env.SPRING_PROFILE}"
    ]
}

8. Troubleshooting Common Issues

8.1 Build Failures

  • Solution: Clean Docker cache:
docker system prune

8.2 Port Conflicts

  • Solution: Check application.properties:
server.port=8080

8.3 Authentication Errors (Private Registry)

  • Solution: Configure Jib credentials:
./mvnw jib:build -Djib.to.auth.username=$USER -Djib.to.auth.password=$TOKEN

Conclusion

Dockerizing Spring Boot without a Dockerfile is faster, more secure, and easier to maintain.

  • For simplicity: Use Spring Boot Plugin.
  • For smallest images: Use Jib.
  • For cloud-native apps: Use Buildpacks (Pack CLI).
  • For local dev: Use Docker Compose.

By adopting these methods, you streamline deployments while keeping images lightweight, secure, and production-ready

Leave a Reply

Your email address will not be published. Required fields are marked *