Safer Image Builds with Cloud Native Buildpacks and Wolfi
The software supply chain encompasses everything from programming to deployment, including (open source) libraries, build tools and even the servers it runs on. Each stage presents a potential weak link, and the current landscape is far from secure. Attacks on the supply chain are on the rise, with a 650% increase reported in 2021. The following are the major pain points that software supply chain security introduces:
- Open source dependencies: Heavy reliance on open source code, but keeping track of its vulnerabilities and managing updates can quickly become a tangled mess.
- Tool sprawl and misconfiguration: Every developer, software architect and CTO has their favorite tools, but a hodgepodge of them, along with improper configuration, tends to introduce gaps that can cause a security breach.
- Opaque and complex workflows: Modern software development involves intricate pipelines and automation, making it difficult to pinpoint where things go wrong.
The consequences of not securing the software supply chain can be dire — data breaches, financial losses and reputational damage, to name a few outcomes. Therefore it is important to secure the components that make up the software supply chain. Containers are the most popular unit of deployment currently. In the cloud native world, they’re especially significant.
Yet, their layered structure — with code, libraries and dependencies — amplifies the attack surface. Each layer carries potential vulnerabilities, multiplying risk. Tracking and patching these vulnerabilities across a fleet of containers becomes a game of whack-a-mole, making the supply chain a tangled web of security concerns.
What Is Wolfi?
Wolfi is a Linux distribution, the aim of which is to provide a secure base layer for container images. It is popularly marketed as an “undistro.” It is envisioned, built, and maintained actively by the Chainguard community, a lot of whom are seasoned software security professionals. Collectively, they aim to keep Wolfi CVE-free!

I learned to make use of Wolfi by reading docs related to BusyBox and Alpine. Both Alpine Linux and BusyBox are renowned for their minimalist approach to the Linux world. They prioritize small footprints, efficiency and security, making them ideal for resource-constrained environments like containers, embedded systems and servers — and the design principles for Wolfi borrow heavily from these. Wolfi has been designed from the ground up to support modern computing models such as containers.
What Are Cloud Native Buildpacks?
Buildpacks are a way to create containers from source code. In that sense, they’re an alternative to `docker build`. They do not make use of constructs such as Dockerfiles, but provide a ready-made means to containerization. Cloud Native Buildpacks (CNB) are a specification for Buildpacks which means that they govern certain aspects of how a Buildpack is designed and mandate that they help create OCI-based containers.
A good example of Cloud Native Buildpacks that are also production-ready is Paketo.
Internally, Buildpacks make use of a build image and a run image to create containers. These images are used as layers inside the final container and therefore contribute significantly to the security of the final container. Using Wolfi as the build image and/or run image helps reduce the CVE count of the final image produced.
The best way to make use of Buildpacks is by using pack — a command line interface — that allows a user to create a container from source code.
What Is the Benefit of Using BuildPacks and Wolfi Together?
There are several. First of all, the containers are of much better quality. Both Buildpacks and Wolfi bring significant benefits, together, the nature of the images and containers are totally transformed, as compared to using Docker and Jammy, in this process. Using Wolfi provides two distinct advantages: the images are smaller in size and Wolfi contributes t0 CVEs. Using Buildpacks helps create images that are easier to automate, more modular and simpler to work with compared to those created using Docker.
Tutorial
Prerequisites:
Verify that your installation is working properly by running simple commands for both tools. For example,
Here are the steps to create a container for a sample application:
Create a Base Image
This step has two parts. First, write a Dockerfile. This Dockerfile sets up a base image with a specific user and group configuration for building and running applications (built using Cloud Native Buildpacks) within a containerized environment. It also provides necessary CNB target information for compatibility.
Create a Run Image
Similar to the first step, define a Dockerfile for the run image and run a docker build command to build it. We will be referencing this image in later stages.
As you will notice, this is a nearly identical Dockerfile. Next, run the build command.
Create a Builder
This step also requires a configuration file to be written first. This file, called the builder.toml, will specify a configuration schema that will be used for the whole Buildpack lifecycle.
This builder.toml configuration file does three things. First, it includes two Java-related Buildpacks, prioritizing a local one for Maven-based applications and one from Paketo — a family of open source, production-ready buildpacks. Next, it defines the order for Buildpack detection during the build process. Finally, it includes references to a specific base image for building and a specific run image for executing applications (both of which we created in the preceding steps).
Create a builder utilizing this configuration (builder.toml) by using the following command.
Use the Builder to Export a Container
Use the build subcommand with pack to create a container. Remember to point to the source code.
The builder will analyze the source code of the application to determine its type and dependencies, based on which the builder will apply a sequence of Buildpacks, each responsible for contributing specific layers to the final image. The Buildpacks will create the necessary layers for the image, including dependencies, runtime components and the application itself. The layers will then be assembled into a complete container image, resulting in the image named ramiyengar/wolfi-java.
How to Put This to Use
First, start to make use of Buildpacks in your container creation workflows. Engineering teams and operations will both see tremendous benefits in what Buildpacks have to offer. Buildpacks for every language and framework are available. They are also composable into composite forms which can build images for apps written in more than one language. Once you’re comfortable with the way Buildpacks export their images, move on to optimizing them further with leaner and more secure base images.