What are Buildpacks? Use cases, benefits, and a hands-on tutorial
What is Buildpacks?
Buildpacks is a CNCF (Cloud Native Computing Foundation) project which enables building container images from your application source that can run on Docker, Kubernetes or any cloud-based container orchestration platform. It does so by automatically detecting things like which language your application was written in (i.e. by detecting files like package.json, requirements.txt or go.mod) and what dependencies your app uses (by reading the contents of those files) and automatically choosing a build-process that suits the detected information. It does not require a developer to provide a separate Containerfile that describes the process of building a container.
Benefits of using Buildpacks
The main benefits of using Buildpacks are as follows
- No Containerfiles to manage - You don't need to maintain a separate Containerfile anywhere which describes how your application should be built. Buildpacks read the context i.e. your source code and automatically pick up details of your application and create runtime image accordingly. That also means there is no Dockerfile to keep in sync, so source and image definition can't drift apart.
- Safer build process - A build process that happens using buildpacks can generally be considered as a safer process than a typical docker build, because it does not involve choosing a base image and doing any operations on that base image to create layers. A buildpack build-process generally just involves downloading dependencies needed for the application + compiling and creating a binary of your application if it is a compiled language source code and then attaching the final result to a runtime image, which the builder already has configured. The build process does not require root at any level.
- Flexibility of changing base layers of runtime image - Because of the build process defined in the previous point,
you get the flexibility to use the
rebasefeature. It allows on-demand swapping of the base layer of any runtime container image built with buildpacks, without running the entire build pipeline. This lets you quickly swap the base OS of an existing image, for reasons such as:- Updating to a newer OS version if one is available.
- Patching images that have critical vulnerabilities.
What is it not?
- It is not a replacement for standard ways to build a container.
- It does not always produce the most optimized container image.
- It will not always work out of the box for your custom source code structure. (It can work, but you will need to create your own buildpacks for it 😉)
Buildpacks actually predates Docker and Kubernetes. It was developed by Heroku (now part of Salesforce) to support deployments on its Cloud Platform. It allowed deployment of applications directly on servers without a developer needing to know the process with which it happens. Buildpacks was a way for them to take the source code, package it into something deployable and then run it on their dynos (isolated containers). It was one of the first PaaS (Platform as a Service) offerings. Here's a link to the blog post that announced Buildpacks.
(optional and unrelated) - My first API implementation that I deployed on a Cloud Platform.
It was deployed on Heroku
Here's the link - https://github.com/gat786/quiz_api
Hands on demo time
Building applications
Click on the START TUTORIAL button and we can get going.
You will see that the pack CLI is installed and available for you in the environment. It is the tool used to build images with buildpacks.
Let's invoke the first pack command.
pack
Let's see what version of the pack CLI is available -
(if it is a very old one, someone will need to upgrade it.)
pack --version
You will also see that there is an examples directory. It contains the golang-hello, python-hello and nodejs-hello sample apps.
We will try to build container images for these fairly simple applications using pack.
Go to the examples/golang-hello directory and then run the following command
pack build golang-hello --builder heroku/builder:26
In the above command - golang-hello is the docker image name that pack would create. The --builder flag specifies which builder pack uses in order to complete the build process. We are using the builder provided by Heroku. (Ref - https://github.com/heroku/cnb-builder-images). A typical pack command only requires you to specify a builder and an image name. The directory in which you run the command is automatically used as the build context.
Once the build process completes, we can then run the image using
docker run -p 8080:8080 golang-hello
Test the built API endpoint by opening another terminal and running
curl localhost:8080
Since this is a standard Docker image, it can be tagged and pushed to any registry. Let's do that.
docker tag golang-hello registry.iximiuz.com/golang-hello:latest
docker push registry.iximiuz.com/golang-hello:latest
Similarly, you can build images for the python-hello and nodejs-hello directories.
Investigating the build process
If you look closely at the build logs, you will see (this is for the golang-hello build process, but other languages will have similar outputs).
- It downloads the builder first, i.e.
heroku/builder:26. The entire process afterwards runs inside this container image, with the context directory mounted in. - It runs lifecycle steps
i.e. the lines that begin with
===>and have titles likeDetecting,Analyzing,Restoring,BuildingandExporting. These are building blocks that are present within the builder image. - The
DetectingandBuildingphases are the most important among the ones listed above, because this is where the builder actually detects what needs to be done and then executes theBuildingsteps depending on that. - You will see that during the
Buildingphase, two buildpacks took part. First one is the official language buildpack, in this caseHeroku Go BuildpackandProcfilebuildpack.Procfileis a simple text file present in the application directory which contains textweb: golang-hello. i.e. the runtime application is a webservice and it runs by executing the binary namedgolang-hello. You can read more about it here.
About the Author
More tutorials you might like

How Container Filesystem Works: Building a Docker-like Container From Scratch
Learn how Linux containers are built from the ground up. Starting with the mount namespace and a root filesystem, see why PID, cgroup, UTS, and network namespaces naturally follow - and how this foundation makes concepts like bind mounts, volumes, and persistence in Docker or Kubernetes much easier to grasp.

How Container Networking Works: Building a Bridge Network From Scratch
Begin with the basics to understand Docker and Kubernetes networking: learn how to create and interconnect Linux network namespaces using only command-line tools.

How Kubernetes Reinvented Virtual Machines - In a Good Sense
How Virtual Machines were used to deploy services. What old problems containers solve and what new problems create. How Kubernetes used containers to recreate Virtual Machines in a better way?

How Container Images Actually Work: Layers, Configs, Manifests, Indexes, and More
A practical deep dive into container image internals that will help you build a clear mental model of how images are composed, identified, stored, and distributed across registries.
Learn by doing, not just by reading or watching
Sign up for a free account to start a VM playground right on this page, track your progress, and get notified about new learning materials.