We are happy to announce the release of a new hardened builder for Paketo Buildpacks. The hardened builder is based on BellSoft Hardened Images bringing teams improved security and compliance posture through low-to-zero CVEs design, continuous patching, SBOMs, digital signatures, and SLA. The hardened builder is available for Java, GraalVM Native Image, Python, Go, Ruby, and Node.js.
This article describes how teams will benefit from a new hardened builder and how to get started with it.
The Security Cost of Inconsistent Dockerfile Practices
A good Dockerfile can produce a small, maintainable, secure image. But the trouble starts when every team writes its own Dockerfile. In this case, security depends on developers applying the same practices correctly across every service. That rarely holds.
Small differences add up. One service uses an outdated base image; another ships build tools and unused OS packages; a third runs as root. Our survey found that more than 60% of developers do not realize a poorly written Dockerfile can be a security vulnerability. That is not a failure of effort. Application developers focus on the application, and container hardening is not their main job.
A badly written Dockerfile is one problem. Another is maintaining hundreds of Dockerfiles. When a base image vulnerability appears, security or platform teams must find affected services, get each team to update, rebuild, test, and redeploy. Base image sprawl makes the CVE picture even messier and compliance harder to prove.
Buildpacks replace those scattered decisions with a shared build process. BellSoft’s hardened builder adds a securely maintained, continuously patched OS and runtime baseline.
BellSoft’s Hardened Builder: A Standardized Approach to More Secure Application Image Builds
BellSoft has already been providing its own open source builder for Paketo buildpacks with Java support based on minimalistic Alpaquita Linux with musl and glibc variants. The hardened builder builds on the momentum by extending the language support beyond Java to Python, Go, Ruby, and Node.js and moving the run image to BellSoft Hardened Images.
As a result, BellSoft’s hardened builder provides:
- Standardized builds across languages;
- Minimal, reproducible OCI images without the need to Dockefiles;
- Hardened image baseline with low-to-zero CVEs at release time;
- Continuous patching;
- Provenance data with SBOMs and digital signatures.
This way, teams will benefit both from standardization offered by buildpacks in general and the hardened base for their applications. Instead of requiring every service team to define and maintain its own Dockerfile, platform teams can provide a common build path across supported languages. The buildpacks handle the repeatable parts of image creation, while BellSoft Hardened Images provide a hardened, continuously patched foundation for the resulting runtime image.
This gives organizations a more consistent starting point for container security. Teams can reduce base image sprawl and apply common build and runtime practices. In addition, rather than coordinating separate Dockerfile changes across many repositories, teams can follow a common rebuild or rebase workflow when updates are available.
Supported Languages and Availability
BellSoft’s hardened builder is available on Docker Hub for x86-64 and ARM64 architectures for the following languages and ecosystems:
- Java
- GraalVM Native Image
- Python
- Node.js
- Go
- Ruby
The builder is available in two C library variants musl and glibc. Each language uses the same general workflow: select the relevant buildpack, build the application image, test it, push it to your registry, and deploy it through your existing platform.
Java
BellSoft’s hardened builder for Java is based on Liberica JDK Lite optimized for the cloud, which helps to reduce the final image size for Java applications by at least 30% compared to standard OpenJDK images. The builder supports all LTS Java versions, 8, 11, 17, 25, and the latest JDK version.
To build a Java container image with musl-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:musl --path <source-code>
To build a Java container image with glibc-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:glibc --path <source-code>
See the GitHub project page for the list of available configuration options.
Also, to learn how to use and configure buildpacks with Spring Boot, see this article.
GraalVM Native Image
BellSoft’s hardened builder is based on Liberica Native Image Kit, a downstream version of GraalVM CE with Liberica JDK as the underlying JVM. Liberica NIK Versions 22 and 23 are supported.
To build a native image executable out of a Java application with musl-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:musl --path <source-code> --env BP_NATIVE_IMAGE=true
To build a native image executable out of a Java application with glibc-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:glibc --path <source-code> --env BP_NATIVE_IMAGE=true
For more information about building native images and available options, see Build an App as a GraalVM Native Image Application.
Python
BellSoft’s hardened builder for Python supports Python version 3.14.
To build a Python container image with musl-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:musl --path <source-code>
To build a Python container image with glibc-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:glibc --path <source-code>
For more information about building Python applications and available options, see How to Build Python Apps with Paketo Buildpacks.
Go
BellSoft’s hardened builder for Go supports Go version 1.26.
To build a Go container image with musl-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:musl --path <source-code>
To build a Go container image with glibc-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:glibc --path <source-code>
For more information about building Go applications and available options, see How to Build Go Apps with Paketo Buildpacks.
Node.js
BellSoft’s hardened builder for Node.js supports Node.js version 24.
To build a Node.js container image with musl-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:musl --path <source-code>
To build a Node.js container image with glibc-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:glibc --path <source-code>
For more information about building Node.js applications and available options, see How to Build Node.js Apps with Paketo Buildpacks.
Ruby
To build a Ruby container image with musl-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:musl --path <source-code>
To build a Ruby container image with glibc-based Alpaquita, run:
pack build demo-app --builder bellsoft/buildpacks.builder:glibc --path <source-code>
For more information about building Ruby applications and available options, see How to Build Ruby Apps with Paketo Buildpacks.
How to get Started with BellSoft’s Hardened Builder for Paketo Buildpacks
The new hardened builder is used by default, no need to choose it explicitly. However, depending on where you are at using buildpacks — already using BellSoft’s builder, using buildpacks with other builders, or using Dockerfiles — the getting started guide will be slightly different.
Already Using BellSoft’s Builder
If you already use BellSoft’s builder, you need to rebuild the application image from source with the current builder and pull policy set to always. For instance, here’s how to do that with the pack CLI:
pack build <app-image> --builder bellsoft/buildpacks.builder:<libc-variant> --pull-policy always
The --pull-policy always option ensures pack checks the registry instead of using the builder image cached on the runner. Therefore, this pulls the latest builder and its current buildpacks before rebuilding the image.
If you use buildpacks via Maven/Gradle plugin, you can specify the pull policy there.
Note that you need to regularly update the images so that the fresh base with fixes and patches is used. You can use the command above or use rebase.
Switching to BellSoft’s Builder from Another Builder
If you already use buildpacks and want to switch to BellSoft’s hardened builder, you need to specify it with the --builder option. At the same time, you need to choose whether you want to use a musl- or glibc-based variant. The container image built with the musl-based builder will be slightly smaller than the one built with glibc. We recommend trying both of them and selecting the variant that performs best for your workload and fits your dependencies.
Here’s the command for building an image with a musl-based BellSoft builder:
pack build <app-image> --builder bellsoft/buildpacks.builder:musl
Here’s the command for building an image with a glibc-based BellSoft builder:
pack build <app-image> --builder bellsoft/buildpacks.builder:glibc
For Java applications, if you use buildpacks with Maven or Gradle plugins, you can specify the builder in the plugin configuration.
Maven:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>bellsoft/buildpacks.builder:musl</builder>
</image>
</configuration>
</plugin>
Gradle:
bootBuildImage {
builder = "bellsoft/buildpacks.builder:musl"
}
Migrating to Buildpacks from Dockerfiles
If your team has no prior experience with buildpacks, don’t migrate all workloads at once. Start with one non-critical service. Regardless of the language, the command for building a container image is the same. The builder detects the language automatically.
Build the image from source with the BellSoft’s builder either using the pack CLI:
pack build <image-name> --path <path-to-source> --builder bellsoft/buildpacks.builder:<libc-variant>
Where <libc-variant> is either musl or glibc. Alternatively, use the Maven/Gradle plugin if you build Java applications. For example, here’s how to use buildpacks with Spring Boot.
Maven:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>paketo-maven</name>
<builder>bellsoft/buildpacks.builder:musl</builder>
</image>
</configuration>
</plugin>
</plugins>
</build>
Build the image with:
mvn spring-boot:build-image
Gradle:
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.6'
id 'io.spring.dependency-management' version '1.1.0'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
bootBuildImage {
imageName = "paketo-gradle"
builder = "bellsoft/buildpacks.builder:musl"
}
Build the image with:
gradle bootBuildImage
After building the container image, run the usual functional and integration tests, and compare the result with the current Dockerfile-based image. Check that the application starts correctly, exposes the expected ports, reads its configuration, and works with the existing deployment pipeline.
After successful validation, you can start migrating other services using the same process.
When updated hardened base images become available, rebuild or rebase the application image and redeploy it through the normal release process. Note that the update requires an explicit action from the team, but the workflow is consistent across supported applications.
Conclusion: A Hardened Default for Application Container Builds
Container security should not rely on every team maintaining a flawless Dockerfile. BellSoft’s hardened builder for Paketo buildpacks gives Java, Python, Go, Ruby, and Node.js teams a standard way to build application images on a hardened, continuously patched foundation.
Try BellSoft’s hardened builder for your application image and start from a more secure baseline. If you have any questions about BellSoft's builder or other products or would like to request support, contact us, and our engineers will be happy to help you.







