BuildKit’s ability to export build artifacts as a tar archive is a powerful tool for packaging and distributing your application’s compiled assets.
Let’s see it in action. Imagine you’ve built a simple Go application inside a Docker container. You want to extract the compiled binary, not the entire container image.
# syntax=docker/dockerfile:1
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .
FROM scratch
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
Now, let’s build this using BuildKit and export only the myapp binary.
DOCKER_BUILDKIT=1 docker build -t myapp-image .
docker build --output type=tar,dest=myapp.tar myapp-image
This docker build --output type=tar,dest=myapp.tar myapp-image command does the magic. It tells BuildKit to perform the build for myapp-image and then, instead of creating a standard Docker image, it will produce a tar archive named myapp.tar containing the exported build artifacts.
The mental model here is that BuildKit, by default, builds an OCI-compatible image. However, the --output flag allows you to intercept this process and define what gets exported and how. The type=tar option specifically directs BuildKit to package the final filesystem state of the build’s target stage into a tarball. The dest= option specifies the filename for this tarball.
Internally, BuildKit stages the build process. When you specify --output type=tar, BuildKit takes the resulting filesystem of the final stage of your build (or a specified stage if you use source=...) and serializes it into a tar archive. This is distinct from exporting the entire Docker image, which would include all layers, metadata, and manifest information. Here, we’re getting just the files that were present at the end of the build, exactly as they would be in the root of the container.
The key levers you control are:
type: This dictates the output format.taris one option, but others likelocal(for a directory) orregistry(to push an image) exist.dest: The path and filename for the exported artifact (fortype=tarortype=local).source: (Optional) If you want to export artifacts from an intermediate stage rather than the final one. For example,source=builderwould export the contents of thebuilderstage.mode: (Optional) Fortype=tar, this can beminormax.minexports only the exported layers, whilemaxexports everything. For most use cases,minis sufficient and results in smaller archives.
When you extract myapp.tar using tar -xf myapp.tar, you’ll find the myapp binary directly in the root of the extracted directory. This is because the scratch image in our Dockerfile starts with an empty filesystem, and we’re copying myapp into its root.
What most people don’t realize is that the type=tar export mechanism is incredibly flexible and can be used to extract any build artifact from any stage. You’re not limited to the final output of a scratch image; you could, for instance, export a build directory from a compilation stage into a tarball for later analysis or use in another build process, all without needing to build a full Docker image.
The next step is often to use these exported artifacts in a subsequent build or deployment pipeline.