Adding custom certificates with Custom Assembly
How to add custom certificates to customized images with Custom Assembly.
For the complete documentation index, see llms.txt.
Chainguard Containers ship with only the packages their application needs, so sooner or later you’ll want one that isn’t there. Custom Assembly is the supported way to add it. You declare the package you want, Chainguard builds the image on its own infrastructure, and Chainguard rebuilds that image whenever the package is updated. You can drive Custom Assembly from the Chainguard Console, interactively with chainctl, or non-interactively with chainctl from a pipeline.
This page helps you pick an approach, then covers the three steps that apply whichever one you pick: finding the package name, adding the package, and confirming that it reached the finished image.
The following table compares the available approaches:
| Approach | Use it when | What you need |
|---|---|---|
| Custom Assembly in the Console | You want to browse the packages your organization can add and apply the change in a few clicks. | A Console account with a role that has the repo.update capability. |
Custom Assembly with chainctl, interactively | You work from a terminal and want to review a diff before it applies. | chainctl, installed and authenticated. |
Custom Assembly with chainctl, non-interactively | You keep image configuration in version control or apply it from CI/CD. | chainctl and a YAML build configuration file. |
| Custom Assembly with the Chainguard API | You’re building your own tooling around Custom Assembly. | An API client and a Chainguard token. |
apk add in a Dockerfile | You build on a -dev variant or on wolfi-base, and you’re prepared to pin package versions and image digests yourself. | A Dockerfile and a container image that includes apk. |
apk with chroot in a multi-stage build | You need a package in a distroless image and Custom Assembly doesn’t fit your workflow. | A multi-stage Dockerfile. |
Custom Assembly is the recommended approach because Chainguard’s build pipeline resolves the packages you add against the packages already in the base image, then rebuilds the image when any of them change. Adding packages with apk add in your own Dockerfile moves that work to you: without pinned package versions and image digests, a package update can conflict with an older dependency in the base image and break your build until a new base image is released. For the longer version of this argument, see Why use Custom Assembly for adding packages.
Note: Custom Assembly is available to organizations with access to production Chainguard Containers. If you use Chainguard’s free container images, take one of the Dockerfile approaches.
The Custom Assembly approaches on this page share these prerequisites:
repo.update capability, to customize an existing image in place, or the repo.create capability, to save the result as a new image. Of Chainguard’s three default roles — viewer, editor, and owner — only owner has both. For a custom role you can create instead, see Custom Assembly permissions requirements.chainctl approaches, chainctl installed and authenticated.Custom Assembly adds packages to an image; it can’t remove the packages the source image already contains. You can, however, remove packages you added in an earlier build.
You can add only the packages your organization is entitled to, which are the packages that appear in the Chainguard Containers you already have access to. Package names often carry a version stream — python-3.14 rather than python — so confirm the exact name before you add it. Look it up in the Console, or with apk from inside a running container.
Open the image in the Chainguard Console, click Customize image, then use the Filter packages box. The list holds every package your organization can add. If the package you want isn’t listed, open a Chainguard support ticket.
Container images that include apk — such as a -dev variant — can search the repository from inside a running container. For a free container image, no authentication is needed:
docker run --rm --entrypoint sh cgr.dev/chainguard/wolfi-base:latest \
-c 'apk update > /dev/null && apk search -e "mongo*"'mongo-tools-100.18.0-r6
mongodb-kubernetes-operator-0.13.0-r14
mongodb-kubernetes-operator-compat-0.13.0-r14
mongodb-kubernetes-operator-readinessprobe-0.13.0-r14To search the packages your organization is entitled to, start a -dev variant of one of your organization’s images with an HTTP_AUTH variable so that apk can reach your Private APK Repository:
docker run -it --rm --entrypoint /bin/sh --user root \
-e "HTTP_AUTH=basic:apk.cgr.dev:user:$(chainctl auth token --audience apk.cgr.dev)" \
cgr.dev/$ORGANIZATION/$CONTAINER:latest-devFrom the container’s shell, run apk update and then apk search.
The three procedures below all produce the same result. Pick the tab that matches how you work.
For the full walkthrough, including how to edit or remove customizations later, see Using the Chainguard Console to manage Custom Assembly resources.
Open the image’s build configuration:
chainctl images repos build edit --parent $ORGANIZATION --repo $CONTAINERReplace $ORGANIZATION with your organization’s name and $CONTAINER with the name of the image. If you omit either flag, chainctl prompts you to choose.
chainctl opens the configuration in your default text editor. Add the package under contents.packages:
contents:
packages:
- yarn
- wget
- bashSave and close the file. chainctl prints a diff and asks you to confirm:
/tmp/3352123767.yaml (-deletion / +addition):
contents:
packages:
- yarn
- wget
+ - bash
Applying build config to $CONTAINER
Are you sure?
Do you want to continue? [y,N]:Enter y to start the build.
To save the result as a new image rather than changing the existing one, add --save-as $NEW_NAME. For the rest of what you can set in this file, including environment variables, annotations, and custom user accounts, see Using chainctl to manage Custom Assembly resources.
Both apply and edit accept a configuration file, which skips the editor and the prompt. Use this form in CI/CD and anywhere you keep image configuration in version control.
Write the build configuration to a file:
cat > build.yaml <<EOF
contents:
packages:
- bash
- curl
- mysql
EOFPreview what the file would change, without changing anything:
chainctl images repos build apply -f build.yaml --parent $ORGANIZATION --repo $CONTAINER --dry-run--dry-run prints the diff and exits with a non-zero status if there’s anything to apply, which makes it usable as a drift check in a pipeline.
Apply the configuration. --yes confirms the change without prompting:
chainctl images repos build apply -f build.yaml --parent $ORGANIZATION --repo $CONTAINER --yesTo save the result as a new image, add --save-as $NEW_NAME. This works when you target a single repository; it isn’t available when you target several at once with repeated --repo flags or a wildcard.
For a worked GitHub Actions pipeline built around these commands, see Using GitOps to manage Custom Assembly resources.
Custom Assembly builds run on Chainguard’s infrastructure and normally finish in under 20 minutes, so your change won’t reach the registry immediately.
Check that the build succeeded:
chainctl images repos build list --parent $ORGANIZATION --repo $CONTAINER START TIME | COMPLETION TIME | RESULT | TAGS
-------------------------------|-------------------------------|---------|-------------------------------
Wed, 09 Sep 2026 12:46:56 CDT | Wed, 09 Sep 2026 12:47:13 CDT | Success | 26-full, 26.8-full, latest-full
Wed, 09 Sep 2026 12:45:22 CDT | Wed, 09 Sep 2026 12:46:13 CDT | Success | 26-dev, 26.8-dev, latest-devThe Console shows the same information on the image’s Builds tab. Builds stay listed for 24 hours.
Pull the image:
docker pull cgr.dev/$ORGANIZATION/$CONTAINER:latestCheck for the package. If the image includes apk, query it directly. apk info -e prints the package name when the package is installed and exits with a non-zero status when it isn’t:
docker run --rm --entrypoint apk \
cgr.dev/$ORGANIZATION/$CONTAINER:latest-dev info -e bashbashDistroless images have no apk, so read the image’s SBOM instead. This command lists the apk packages in the image:
cosign download attestation \
--platform linux/amd64 \
--predicate-type https://spdx.dev/Document \
cgr.dev/$ORGANIZATION/$CONTAINER:latest \
| jq -r '.payload' | base64 -d \
| jq -r '.predicate.packages[]
| select(.externalRefs[]?.referenceLocator? // "" | startswith("pkg:apk/"))
| .name'ca-certificates-bundle
gdbm
glibc-2.44
ld-linux-2.44
python-3.14The Console shows the same list on the image’s SBOM tab. For more ways to read this data, see Retrieving Chainguard Container SBOMs.
A Custom Assembly build reports failure only after it finishes. Retrieve the logs for a build with the logs subcommand, which prompts you to pick a build report:
chainctl images repos build logs --parent $ORGANIZATION --repo $CONTAINERIn the Console, click a row on the image’s Builds tab to open the same logs.
Builds fail for a few recurring reasons:
glibc.For more on these cases, see Custom Assembly troubleshooting. If you can’t resolve a failure, contact Chainguard support.
Last updated: 2026-09-10 12:23