Spring Boot secured base runtime images

August 27, 2025

When working with Java and the Spring Boot ecosystem, it's very easy to have the system leverage the Paketo build packs and generate a container image for you. This is a well structured process, simplifying the container build and providing a number of optimizations.

What are some benefits? Well, to start, if you don't have complex container requirements, you can skip writing a Dockerfile and just let spring boot generate the container from your application. There is a certain amount of flexibility in the container build process, so you can tune and tweak things, although not to the level of a full Dockerfile based build. Still, many applications are perfectly fine tuning and tweaking the edges, and can avoid the overhead of creating and managing a secure Dockerfile-based build process.

When building multiple containers, the build pack process can use layer optimization to reuse common components and items between builds, and when doing a quick patch, may only require a small slim layer to be updated and redistributed. This can lead to a real savings in space and bandwidth when distributing container images, especially when considering a global reach.

To better understand the Paketo ecosystem and process check out: https://paketo.io

Now by default, the build process brings along its own base runtime for your application, and in many cases this may be totally fine. The goal of this article is to identify the relatively easy process to have more control, visibility, and accountability for your runtime container though. Fortunately, this is accounted for in the spring boot container build process.

Starting with a basic Maven build plugin configuration, you can see how an image is specified, including the repository:

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <image>
                <name>container.registry/lemuridaelabs/app-services:${project.version}</name>
            </image>
        </configuration>
        ....
    </plugin>

With this configuration, running: mvn package spring-boot:build-image will generate the application and the container for deployment. Be sure that your application is set to an executable jar deployment and not war file.

So with this foundation, what's next? In a recent article we reviewed the secured base images provided by Lemuridae Labs every day. These images can be used in this configuration to keep the benefits of the build packs, while using a slim, secured, and signed base image.

The article on the secure base Java images can be found at: https://www.lemuridaelabs.com/post/secured-java-container-images

To make this change, add the runImage directive to the configuration, and specify which Lemuridae Labs image works for you! An example is shown below:

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <image>
                <name>container.registry/lemuridaelabs/app-services:${project.version}</name>
                <runImage>lemuridaelabs/openjdk-java24-jre:latest</runImage>
            </image>
        </configuration>
        ....
    </plugin>

The change is minor, but with this the application is built, assembled, and ready to go with the best of both worlds. A comprehensive build process combined with a secure and accountable base runtime.

Other Enhancements

When building in more recent Java versions, you can take advantage of AOT processing to reduce memory usage and improve application startup time. This is easy to incorporate into a build process, however it is not a silver bullet and care must be taken to ensure that your application and it's dependencies are compatible with this processing. There are limits, such as runtime class path updates, that will not work properly when running in an AOT-processed configuration.

That said, it may be worth a shot to see how your application performs with this configuration. To give it a try, run:

mvn clean compile spring-boot:process-aot package

This will clean and build your application and then generate the AOT runtime information, prior to creating the packaging information. After this, add an AOT directive to your image configuration in the pom.xml file above, like:

    <env>
	    <BP_SPRING_AOT_ENABLED>true</BP_SPRING_AOT_ENABLED>
    </env>

When this is added to the <image/> block, it will direct Paketo to incorporate the AOT information generated in the modified build process. You should be able to see messages in the build process highlighting the AOT information being incorporated.

If you try this out.. test your application carefully! Not all applications and configurations will work well with this, but if it does, you can gain some real benefits without a lot of effort. Not bad!

Building Success,
One Project at a Time.
Today is the day we can build something together, expanding and collaborating to create something new.
Start Now