skip to content

OCI images via buildpacks

bootBuildImage produces an OCI image with no Dockerfile, using Paketo buildpacks to choose the base image, JDK and JVM settings. Interviewers ask you to compare it with a hand-written Dockerfile for control versus convenience.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

How do you build a container image from a Spring Boot application without writing a Dockerfile, and what mechanism makes this possible?

level: juniorimportance: must knowfreq 70%

answer

  1. bootBuildImage / spring-boot:build-image
  2. Cloud Native Buildpacks, no Dockerfile
  3. Paketo builder default
  4. needs Docker daemon
  5. layered jar = good caching

basics

~10 s

Run the built-in goal: ./gradlew bootBuildImage (Gradle) or ./mvnw spring-boot:build-image (Maven). Spring Boot uses Cloud Native Buildpacks to turn your app into an OCI (Docker-compatible) container image, no Dockerfile needed.

solid answer

~40 s

Spring Boot ships a build-image goal in both plugins: `bootBuildImage` for Gradle and `spring-boot:build-image` for Maven. It uses Cloud Native Buildpacks (by default the Paketo builder image) to produce an OCI-compliant image. The buildpacks inspect your compiled app, detect it's a JVM/Spring Boot app, install a suitable JRE, and layer the exploded jar for good caching — all without you authoring or maintaining a Dockerfile. You need a running Docker (or other) daemon reachable for the build. The result is a normal image you can `docker run`. Compared to a hand-written Dockerfile, you get sensible, community-maintained defaults (memory calculation, security-minded base image, layering) for free, at the cost of some direct control over the image internals.

code

kotlin · 16 lines
kotlin
// build.gradle.kts — the Spring Boot Gradle plugin registers bootBuildImage.
// Run it with:  ./gradlew bootBuildImage

plugins {
    java
    id("org.springframework.boot") version "3.5.0"
    id("io.spring.dependency-management") version "1.1.6"
}

// No extra config needed for a basic build:
// the task depends on bootJar and uses the default Paketo builder.

tasks.named<org.springframework.boot.gradle.tasks.bundling.BootBuildImage>("bootBuildImage") {
    // Optional: give the image a friendly name instead of the project default.
    imageName.set("example.com/team/${project.name}:${project.version}")
}

go deeper

for a junior

Know the two goal names and that buildpacks replace the Dockerfile.

for a middle

Explain the Paketo builder default and that a Docker daemon is required.

for a senior

Discuss layered-jar caching and the detect/build lifecycle.

for a principal

Weigh buildpacks vs hand-written Dockerfiles vs Jib for a platform strategy.

**OCI image** = the Open Container Initiative standard image format — what Docker, Podman, Kubernetes all run. "Build an OCI image" just means "produce a container image." **The problem it solves:** Traditionally you package a Spring Boot app as an executable jar, then hand-write a `Dockerfile` (`FROM eclipse-temurin`, `COPY app.jar`, `ENTRYPOINT java -jar ...`). That Dockerfile is boilerplate you must maintain, keep secure, and optimize for layer caching yourself. **Cloud Native Buildpacks (CNB):** an open specification (originally from Heroku/Pivotal, now a CNCF project) for turning source or compiled artifacts into runnable container images *without a Dockerfile*. A **builder** is an image that bundles a set of **buildpacks** plus lifecycle logic. Each buildpack has two phases: **detect** ("does this app apply to me?") and **build** ("contribute a layer"). For a Spring Boot jar the relevant buildpacks detect a JVM app, install a JRE, and configure the entry point. **Spring Boot's integration:** Both official plugins expose a goal that invokes the CNB lifecycle against a **builder image**: - Gradle: `./gradlew bootBuildImage` (task type `BootBuildImage`). - Maven: `./mvnw spring-boot:build-image` (bound to the `package` phase if you configure it, or run directly). By default it uses a **Paketo Buildpacks** builder (e.g. `paketobuildpacks/builder-jammy-java-tiny` in recent Boot versions; the exact default tracks the Boot version). Paketo is the CNB implementation Spring recommends. **What happens under the hood:** 1. The plugin first builds your application jar (it depends on the `bootJar`/`repackage` output). 2. It pulls the **builder** image and a **run** image (the base the final app runs on). 3. It runs the CNB **lifecycle** which detects the app, downloads a JRE, and **explodes the jar into layers** matching Spring Boot's layered-jar model (dependencies, spring-boot-loader, snapshot dependencies, application classes) so that changing your code only rebuilds the top layer. 4. It writes the finished image to the local Docker daemon (default) under a name derived from your project. **Prerequisites / gotchas:** - A container runtime the plugin can talk to (Docker daemon by default; the connection is configurable via `docker` settings / `DOCKER_HOST`). The build is *not* pure-JVM — it needs that daemon. - The first run is slow (pulls builder + run images); later runs reuse cache volumes. - The default image name is `docker.io/library/<artifactId>:<version>` (Maven) / `docker.io/library/<project.name>:<version>` (Gradle) — often you'll override it. - It produces an image; it does **not** push by default (see the publish option). **When to use:** you want maintainable, secure, well-layered images without owning a Dockerfile — ideal for standard Spring Boot services. **When not to:** you need very specific OS packages, custom base images, or air-gapped builds where pulling Paketo builders is awkward (though these are configurable).

  • Does bootBuildImage require a Dockerfile in your project?
    No. That is the whole point — Cloud Native Buildpacks generate the image layers themselves. You never write or maintain a Dockerfile.
  • Does the build need Docker installed?
    It needs a reachable container daemon (Docker by default). The plugin talks to it via the Docker API; you can point it elsewhere with the `docker` connection settings or DOCKER_HOST. The build is not pure-JVM.

saying these in an interview costs you the question

  • Claiming you still need a Dockerfile for bootBuildImage to work
  • Saying it uses Jib or Dockerfile-based builds (it uses Cloud Native Buildpacks / Paketo)
  • Thinking it runs with no container runtime at all

context

open as a page

How do you control the resulting image's name and push it directly to a registry from bootBuildImage / spring-boot:build-image?

level: middleimportance: must knowfreq 65%

basics

~10 s

Set the image name (imageName in Gradle, <image><name> in Maven, or --imageName= / -Dspring-boot.build-image.imageName=). To push, enable publish (publish = true / <publish>true</publish>) and provide registry credentials via docker.publishRegistry.

open as a page

How do you customize which Paketo builder is used and control things like the JVM version or JVM buildpack settings during a buildpacks build?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Override the builder (e.g. builder = "paketobuildpacks/builder-jammy-base") and/or runImage. Pass build-time settings to the buildpacks via environment map entries, e.g. BP_JVM_VERSION=21 to pick the JRE version.

open as a page

Why do buildpacks-built Spring Boot images cache well, and how does the layered-jar format relate to that? How does a GraalVM native image build differ?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The buildpack explodes the executable jar into Spring Boot's layered structure (dependencies, loader, snapshot deps, application) so changing only your code rebuilds just the top layer. A native build (BP_NATIVE_IMAGE=true) instead AOT-compiles to a single native executable — tiny, fast-starting, no JVM.

open as a page

As a platform owner, how do you decide between buildpacks (bootBuildImage) and a hand-written Dockerfile, and what do you do to make buildpacks builds reproducible and secure in CI?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Buildpacks give maintenance-free, secure, well-layered images and automatic base-image patching, at the cost of control. Dockerfiles give full control but you own security/layering. For reproducibility, pin the builder/run images by digest, control the JVM version, manage registry creds as secrets, and scan the output.

open as a page