CLI tool
buildpacks/pack avatar
buildpacks/pack

pack CLI: Building OCI Images from Source Without a Dockerfile

CLI for building apps using Cloud Native Buildpacks

3,010 stars365 forksGoApache-2.0

At a glance

What is it?
The pack CLI turns application source into a runnable image by orchestrating Cloud Native Buildpacks. It suits teams that want reproducible, patchable images without hand-maintained Dockerfiles, and it assumes Docker is already on the machine.
Who is it for?
Adopt pack if you want image builds driven by buildpacks rather than a Dockerfile, and you already have Docker running locally or in CI. Do not adopt it if your build needs a hand-written Dockerfile with custom system packages, because the buildpack lifecycle owns that layer.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What pack Solves and Who It Is Aiming At

A Dockerfile is a script you maintain. Every base image bump, every language runtime upgrade, and every security patch to the OS layer is a line someone has to edit and re-test. pack takes a different position: the image build is derived from the application's source by a set of buildpacks, and the base layers come from a builder image that someone else maintains. The README frames the audience in three parts. App developers use buildpacks to convert code into runnable images. Buildpack authors develop and package buildpacks for distribution. Operators package buildpacks and maintain applications. Those are genuinely different jobs, and pack tries to serve all three from one binary.

The practical consequence is that pack is not a replacement for a container runtime. It is a client that drives a build. If you have ever wanted a Java or Node service to produce a container image without writing a Dockerfile, and you are willing to accept the buildpack's opinions about how that image is assembled, pack is the tool the Cloud Native Buildpacks project points you at. The README describes it as a CLI implementation of the Platform Interface Specification, which is the important framing: pack is one implementation of a written contract, not a proprietary build system.

The Buildpack Lifecycle Behind a pack build

The repository layout tells you a lot about the architecture. There is a builder/ directory, a buildpackage/ directory, a registry/ directory, and a lifecycle dependency in go.mod pinned to github.com/buildpacks/lifecycle. pack itself does not compile your application. It assembles the inputs, hands them to the lifecycle, and the lifecycle runs the buildpacks inside a container.

The flow, as the specification and the README describe it: you name a builder (an image containing a stack, the lifecycle binaries, and a set of buildpacks), you point pack at your source directory, and pack produces an OCI image. The lifecycle runs in phases. Detection decides which buildpacks apply to your source. Build executes them. Export writes the final image layers. Because the lifecycle runs in a container, the build environment is the builder image, not your laptop, which is where the reproducibility argument comes from.

A builder is the unit of opinion here. Swap the builder and you swap the language support, the OS base, and the runtime versions. That is the trade-off: you get consistency, and you give up fine-grained control over what ends up in the image. The dependency list confirms the scope of the client. It pulls in the Docker CLI and Docker engine libraries, go-containerregistry for registry work, go-git, and Kaniko, which is the piece that lets pack build images without a Docker daemon in some configurations.

Installing pack and Running Your First Build

The README does not inline installation steps. It links to an install page at buildpacks.io/docs/install-pack/ and to a tutorial called An App's Brief Journey from Source to Image. Those two links are the entry point, and the Getting Started section points at the tutorial rather than reproducing it. Because the README gives no package manager commands, none are shown here.

What the repository does show is that pack ships as a container image. The Dockerfile builds the binary with Go and copies it into a distroless static base, with the binary at /usr/local/bin/pack and an entrypoint of the same path. That means you can run pack itself as a container if you prefer not to install a binary on the host.

The build target in the Makefile is the canonical local build, and it stamps a version into the binary through a linker flag on the client package:

bash
make build

The Makefile declares build as the default goal, so plain make does the same thing. The output lands in out/pack on Linux and macOS, and the Makefile sets PACK_BIN to pack.exe on Windows. The version string is assembled from PACK_VERSION plus the short git SHA, so a build from a dirty checkout carries a +git- suffix rather than a clean release number. If you want the released binary instead, the install page linked from the README is where the project directs you.

Once pack is on your PATH and a Docker daemon is reachable, a first build is a single command naming a builder and an output image tag, run from the root of the application source. The README's usage section shows a build animation rather than text, so the exact flags belong to the CLI documentation the README links to. What you should expect to see is the lifecycle running detection and build phases in your terminal, followed by an image reference you can run. If the build fails at detection, the usual cause is that no buildpack in the chosen builder recognizes your source.

Where pack Is the Wrong Choice

The clearest limitation is the one the project's own framing implies. If your application needs a system package that no buildpack installs, or a build step that only makes sense as a sequence of shell commands you control, a Dockerfile expresses that directly and pack does not. There is no documented escape hatch in the README for injecting arbitrary Dockerfile instructions into a pack build. You either find a buildpack that does what you need, write one, or use a different tool.

The second constraint is the builder. Your image's base OS, its runtime versions, and its patch level are properties of the builder image, not of your repository. That is the point, but it also means an upgrade to the underlying OS arrives when you move to a new builder, and that move can change build behavior. Testing a builder change is a real task, not a version bump.

The third is environmental. The dependency list includes the Docker CLI and Docker engine client libraries, and the README's usage section is a terminal recording, so a working container runtime is part of the assumed setup. The repository does include Kaniko, which is associated with daemonless image builds, but the README does not document a daemonless workflow, so treat local Docker as the supported path unless you find it stated elsewhere in the project's documentation.

Finally, the README is thin on operational detail. It does not document rollback, it does not describe how to pin a builder to a digest, and it does not cover what happens when a build fails midway. Those answers live in the linked CLI documentation and the specification, not in the repository's front page.

pack Against a Dockerfile-Based Build

The honest comparison is not pack versus another buildpack tool. It is pack versus a Dockerfile, because that is the choice most teams are actually making. The difference in approach is where the build logic lives. With a Dockerfile, the instructions are in your repository, versioned with your code, and reviewable line by line. With pack, the instructions live in buildpacks maintained by someone else, and your repository holds source plus whatever configuration those buildpacks read.

That flips the maintenance burden. Dockerfile teams own base image updates themselves and get an immediate, visible diff when something changes. Buildpack teams delegate that to the builder maintainer and get a smaller repository, at the cost of a less obvious change surface. Neither is strictly better. A team with unusual system-level requirements will fight the buildpack model. A team running a dozen similar services in one language will spend less time on image plumbing with pack.

Within the buildpack world, the names that come up alongside pack are Paketo buildpacks, which are a set of buildpacks and builders, and the Spring Boot integration, which produces images through the same lifecycle. Those are complements rather than alternatives: pack is the client, and a builder full of buildpacks is the thing it drives. Choosing pack without choosing a builder is not a decision you can make.

Maintenance, Releases and the Apache-2.0 Licence

pack is not archived, and the last push to main was on 2026-09-21, three days before this writing. The release cadence visible in the repository is roughly monthly: v0.40.7 on 2026-06-23, v0.40.8 on 2026-07-13, and v0.40.9 on 2026-08-09. Version numbers are still in the 0.x range, which the project's own release numbering makes explicit rather than hiding.

The upgrade cost is dominated by the builder, not the binary. Moving pack forward is usually a binary swap. Moving the builder forward is the change that can alter your image, and the repository gives no migration notes for that in the README. Plan for a rebuild and a smoke test of the resulting image whenever the builder moves, not just when pack does.

The licence is Apache-2.0, stated in the README badge and present as a LICENSE file at the repository root. Apache-2.0 is a permissive licence with an explicit patent grant and a requirement to preserve notices. It does not impose copyleft obligations on your application. That is a general property of the licence text, not legal advice; if your organization has specific redistribution or notice requirements, read the LICENSE file and the NOTICE handling your legal team expects. The README also carries a FOSSA badge, which indicates the project runs automated licence scanning, though the README does not describe what that scan covers.

Editorial conclusion

Adopt pack if you want image builds driven by buildpacks rather than a Dockerfile, and you already have Docker running locally or in CI. Do not adopt it if your build needs a hand-written Dockerfile with custom system packages, because the buildpack lifecycle owns that layer. Before committing, verify which builder you will use, run one build against your real application, and confirm the resulting image starts with the runtime your framework expects.

Frequently asked questions

How do buildpacks work in pack?

pack hands your source directory and a builder image to the Cloud Native Buildpacks lifecycle, which runs detection to choose applicable buildpacks, executes the build phase, and exports an OCI image. The README describes pack as a CLI implementation of the Platform Interface Specification, and the lifecycle is a pinned dependency in go.mod.

What does "buildpack" mean?

In this project a buildpack is a unit that inspects application source and contributes to producing a runnable image. The README separates the people who use buildpacks to convert code into images from the people who develop and package buildpacks for distribution.

What are the differences between Dockerfiles and buildpacks?

With a Dockerfile the build instructions live in your repository and you maintain the base image yourself. With pack the instructions come from buildpacks inside a builder image that someone else maintains, so your repository holds source rather than build steps, and the base OS and runtime versions move when the builder moves.

Official sources

  1. buildpacks/pack on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/buildpacks-pack.svg)](https://hysenlabs.com/projects/buildpacks-pack)