Chainguard Images: reproducible minimal OCI images built from lock files
Public Chainguard Images
At a glance
- What is it?
- The chainguard-images/images repository holds the public build configuration for the Chainguard image catalogue, where every image is built by apko from a pinned locked_config.json and published to cgr.dev with an SBOM and cosign attestations. It suits an engineer who needs to see exactly what goes into a container image, and it is not where you go to publish your own image.
- Who is it for?
- Chainguard Images fits a team that wants minimal container images with a package list they can read, since every image in the catalogue is built from a locked_config.json checked into the repository and the same lock file reproduces the same content, and the publication pipeline attaches an SBOM and a cosign attestation to each push.
- 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 received new commits within the last day.
- What is it written in?
- Mainly HCL, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A catalogue whose build inputs are public
This repository holds the public build configuration for the available Chainguard Images, which are described as minimal, hardened OCI images published to cgr.dev/chainguard/<image>. The catalogue itself is browsable at images.chainguard.dev, and there is a separate Chainguard Console at console.chainguard.dev for the full commercial catalogue.
The images are pulled like any other OCI image:
docker pull cgr.dev/chainguard/static:latest
docker pull cgr.dev/chainguard/nginx:latestThe interesting part is not the pull command but what is in the repository. Each image directory holds a locked_config.json, a README.md with user-facing documentation for that image, a tests/ directory with end-to-end tests, and a metadata.yaml. The locked_config.json is the source of truth for what goes into the published image, and it contains a fully resolved apko configuration: exact package versions, accounts, entrypoint, environment and annotations, for each supported architecture.
Because the package versions are pinned, the builds are reproducible. The same locked_config.json produces the same image content, which is a stronger claim than most container build pipelines make, where the same source produces a different image whenever a base image moves.
Each image's usage notes live in its own README under the images directory, so the documentation for an image is versioned alongside the configuration that produces it. The topic list names apko, melange, wolfi, oci and audit, and the primary language of the repository is HCL, which is Terraform, because the release process is driven by Terraform.
The licence is Apache-2.0, and the repository is not archived, with the last push on 2026-10-20.
The release workflow shards images and hands off to Terraform
Publication is driven by the release.yaml workflow in the .github/workflows directory. The trigger is a change to a locked_config.json on main, or the daily schedule.
When it runs, the workflow shards the affected images and invokes the Terraform module at main.tf, which does four things in order. It reads images/<name>/locked_config.json, builds each variant with the apko Terraform provider, publishes the resulting OCI images to cgr.dev/chainguard/<name> along with SBOMs and cosign attestations, and then applies the tags declared in the lock file.
Three details in that sequence are worth pausing on.
Sharding means only the images that changed are rebuilt and republished, which is what makes a per-commit or daily release of a large catalogue practical. The apko Terraform provider is the build path in the release, so the same tooling is used whether an image is built by the pipeline or by a developer following BUILDING.md, which documents how to reproduce a build locally with apko and the checked-in lock file.
The SBOM and the cosign attestation are published alongside the image, not generated separately later. That ordering matters for supply chain verification: the attestation describes the artefact that was pushed, and a consumer can verify it against the image digest rather than against a promise made elsewhere.
Tags come from the lock file rather than from the pipeline logic, which means the tag set for an image is reviewed in the same pull request as the package version bump. The repository root also has a .terraform.lock.hcl, so the provider versions are pinned for the release path as well.
Shared Terraform modules and a Makefile for local builds
The repository layout has three moving parts. The images/ directory holds the per-image build configuration, tests and documentation. The tflib/ directory holds shared Terraform modules used by the release driver, so the per-image logic is not duplicated across dozens of image definitions. And .github/workflows/release.yaml is the workflow that runs the release.
The Makefile at the root is for building images outside the release pipeline, and it has two distinct paths. The apko-build target invokes apko directly against a config variable, appending the Wolfi package repository and its signing key and the wolfi-baselayout package, and building for x86_64 and aarch64. A second variant, apko-build-alpine, appends the Alpine edge repository and ca-certificates-bundle instead, which is how an image can be produced against a different package source.
There is also a pattern-style target that builds one image straight from its lock file, keyed on the image name, so make apko-build/git is the command for the git image. The target reads the imageLocks object in the JSON, extracts the configuration for each key with jq, and builds each variant separately, which means a single lock file can hold more than one locked configuration.
Two more targets exist for the Terraform path: one that applies a single image with a given image_name variable, and one that builds every image that has a locked_config.json, sequentially. The Makefile also sets a Terraform plugin cache directory, so provider downloads are cached locally rather than fetched on every run.
Alongside the build tooling there are three list files at the root, reinstated-images.txt, withdrawn-images.txt and withdrawn-repos.txt, and a separate document, WITHDRAWING_IMAGES.md, that describes how tags and repositories are retired. That is a sign of a catalogue with real operational history, since an image that turns out to be broken has to be retired without leaving a dangling latest tag behind.
The rules every image has to follow
The repository documents its own standards, and BEST_PRACTICES.md is linked as the design standards every image follows. The README also points at SECURITY.md for reporting vulnerabilities, at the Chainguard Images documentation at edu.chainguard.dev for background and concepts, and at the .chainguard/ directory in the repository root, which is where the organisation's own configuration lives.
The best-practices document is the piece to read before building anything on these images, because the design standards constrain what an image is allowed to contain. The images are described as minimal and hardened, and the deliberate absence of a shell and a package manager is the point rather than a side effect. That has a direct consequence for how you use them, and it is the thing to plan for before you adopt one: there is no apt-get inside a Chainguard image, and no busybox shell to debug a failed entrypoint with. An image designed to contain only your application is not an image you can log into.
The entrypoint and environment are pinned in the lock file, along with the accounts the image creates. That is a meaningful hardening decision expressed as data. Rather than running a build step that installs a package and hoping it works, the image content is a resolved list, and the accounts that exist in it are declared rather than inherited from a base image.
The annotations are part of the same resolved configuration, and those are what end up in the published image metadata.
There is also a security policy in the repository, which is the expected arrangement for an organisation publishing a large catalogue of artefacts that other people put into production.
Where Chainguard Images is the wrong choice
The first limitation is the minimality itself, and it is not a small one. These are images without a shell and without a package manager by design, which is exactly what makes them small and hard to attack, and exactly what makes them painful to debug. If your operational practice is to exec into a running container and look around, the migration is a change of habit rather than a change of Dockerfile, and the people who will feel it are on call rather than at their desk.
The second is that this repository is the public catalogue, not a publishing platform. The release workflow is set up to build the images in images/ and push them to cgr.dev/chainguard/. Adapting it to publish your own images under your own registry means taking the Terraform modules in tflib/ and the workflow in .github/workflows/release.yaml and reworking them, and the workflow is explicitly about sharding the affected images in this catalogue. The project is a reference implementation with a specific job.
The third is reproducibility, and its limits. The claim is that the same locked_config.json produces the same image content, and that is a claim about the package set being pinned rather than about the whole supply chain. The base packages come from the Wolfi repository, and the keyring used in the local build path is fetched over the network. A lock file pins what is installed; it does not by itself attest where the bits came from, and the cosign attestations published with the images are what cover the output side.
The fourth is that the catalogue moves. The release runs on a daily schedule as well as on changes, and there are no tagged releases in this repository, so there is no version to pin for the configuration itself. If you need to know exactly which image content you were running six months ago, you need to have captured the digest, because the latest tag is a moving target.
The fifth is the commercial boundary. The public repository is the free, minimal set, and the full catalogue sits behind the Chainguard Console. Teams that need the broader set, or the enterprise features around it, are looking at a different product.
Editorial conclusion
Chainguard Images fits a team that wants minimal container images with a package list they can read, since every image in the catalogue is built from a locked_config.json checked into the repository and the same lock file reproduces the same content, and the publication pipeline attaches an SBOM and a cosign attestation to each push. It is the wrong choice if you need images with a shell and a package manager inside them, or if you want to publish your own images, since this repository is the public catalogue rather than a general publishing pipeline. Before pulling one into your build, read the image's own README under images/<name>/README.md and check the locked_config.json for the package versions, and note that the last push was on 2026-10-20 with no tagged releases in this repository.
Frequently asked questions
How do I pull a Chainguard image?
Images are published at cgr.dev/chainguard/<name>, for example docker pull cgr.dev/chainguard/static:latest or docker pull cgr.dev/chainguard/nginx:latest. The full catalogue is browsable at images.chainguard.dev.
What is locked_config.json in the Chainguard Images repository?
It is the source of truth for what goes into the published image, holding a fully resolved apko configuration with exact package versions, accounts, entrypoint, environment and annotations for each supported architecture. Because versions are pinned, the same file produces the same image content.
How are the Chainguard images published?
The release.yaml workflow triggers when a locked_config.json changes on main or on the daily schedule, shards the affected images and invokes the Terraform module at main.tf, which builds each variant with the apko provider and publishes to cgr.dev with SBOMs and cosign attestations, then applies the tags from the lock file.
Can I build a Chainguard image locally?
Yes. BUILDING.md documents reproducing a build with apko and the checked-in locked_config.json, and the root Makefile has an apko-build target plus a pattern target that builds a single image directly from its lock file.
What licence are the Chainguard Images published under?
The repository is licensed under Apache-2.0, with the licence file at the root of the repository.
Official sources
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.
[](https://hysenlabs.com/projects/chainguard-images-images)Was this useful?