# Overview of Custom Assembly Overlays

URL: https://chainguard-docs-preview-git-bot-docs-bundle-refresh.chainguard.app/chainguard/containers/custom-assembly/overlays/overview.md
Last Modified: October 9, 2026
Tags: Chainguard Containers, Conceptual, Custom Assembly

How Custom Assembly Overlays use overlays and bindings to apply customizations to a subset of a repository's tags, or to every repository in an organization or folder.


> **Note**: Custom Assembly Overlays is in beta.


Standard [Custom Assembly](/chainguard/containers/custom-assembly/overview/) applies one customization to every tag in a repository. This fails for images that ship several language or runtime versions side by side, because a package built for one version can't install on the others.

For example, the `python` image publishes tags for Python 3.11, 3.12, 3.13, and 3.14. The `py3.13-typer` package depends on Python 3.13. If you add it with standard Custom Assembly, every tag tries to install it, and the 3.11, 3.12, and 3.14 builds fail.

Custom Assembly Overlays let you choose which tags, and which repositories, receive a customization. For example, you can do the following:

* Add a package to specific tags, such as `3.13` and `3.13-dev`.
* Add debugging tools to every `-dev` tag and keep the other tags minimal.
* Add a package to every tag, with the package name matched to each tag's Python version.
* Reuse one customization, such as your organization's internal certificates, across many repositories.
* Apply a customization to every repository in your organization or in a folder, including repositories created later.

This page explains the concepts. To create and manage customizations, refer to [Manage Custom Assembly Overlays with chainctl](/chainguard/containers/custom-assembly/overlays/chainctl/) or [Manage Custom Assembly Overlays with Terraform](/chainguard/containers/custom-assembly/overlays/terraform/).

## Overlays and bindings

Custom Assembly Overlays split a customization into two resources:

* An **overlay** is a named, reusable set of customizations, such as packages, environment variables, annotations, user accounts, certificates, and runtime repositories. An overlay belongs to your organization, not to a repository, and on its own it changes nothing.
* A **binding** attaches one overlay to one repository, or to every repository under an organization or folder, and selects which tags the overlay applies to.

To apply an overlay to several repositories, create one binding for each repository, or create one [all-repos binding](#all-repos-bindings) that covers them all.

When you create, update, or delete a binding, or update an overlay, Chainguard rebuilds the affected tags without waiting for a new upstream release. An overlay update rebuilds the matching tags in every repository the overlay is bound to. As with standard Custom Assembly, a build normally takes less than 20 minutes, and Chainguard rebuilds the customized tags whenever their packages are updated.

## Tag selectors

Each binding has exactly one tag selector, which chooses the tags the overlay applies to. In `chainctl`, you set it with one of the `--all`, `--variant`, or `--tag` flags on `chainctl images overlays attach`. A selector is one of three kinds:

| Selector | Matches | Example use |
| --- | --- | --- |
| **Exact** | The tags you list by name, such as `3.13` and `3.13-dev`. | Add a package that only works with one version. |
| **Variant** | Every tag of a variant. Only the `dev` variant is available, which matches every tag ending in `-dev`. | Add debugging tools to development images only. |
| **All** | Every tag in the repository. | Add certificates or packages that work with every tag. |

Variant and all selectors also match tags published after you create the binding. An exact selector matches only the tag names you list. Tags that no binding matches keep their uncustomized image.

### Exact tags and shared digests

Several tags often point to the same image. For example, `3.13`, `3.13.7`, and `3.13.7-r0` might share one digest. An exact selector customizes only the tags you list, even when other tags share their digest. If you bind an overlay to `3.13` alone, `3.13` gets a customized image, and `3.13.7` and `3.13.7-r0` keep the original. To keep several tags identical, list all of them.

An exact selector matches tag names, not images. When `3.13` moves to a new release, the binding follows it, so the new `3.13` image is customized too.

Chainguard doesn't check that an exact tag exists when you create the binding. A mistyped tag name matches nothing, so no build runs for it.

## All-repos bindings

A binding normally attaches an overlay to one repository. An **all-repos binding** attaches it to every repository under an organization or a folder instead, including repositories created later. Use one to apply a customization, such as your organization's internal certificates, everywhere at once.

An all-repos binding belongs to the organization or folder it covers, and it has a tag selector like any other binding. An overlay can have at most one all-repos binding per organization or folder. You can't change an all-repos binding's scope; to re-scope one, detach it and attach a new one.

Folders nest, so several all-repos bindings can cover a repository at once: one on the organization and one on each folder above it. For example, an organization can bind a certificates overlay to every repository, and also bind a hardening overlay to its `golden-images` folder. Repositories in `golden-images` receive both.

A repository can't opt out of an all-repos binding. To vary a setting for one repository, bind another overlay to that repository directly: the repository's own bindings take precedence, as described in the next section. All-repos bindings also don't apply to repositories that use standard Custom Assembly; those keep their standard customization.

## How overlapping bindings combine

A tag can match more than one binding. For example, `latest-dev` matches an all binding, a dev variant binding, and an exact binding that lists `latest-dev`. When a tag matches several bindings, Chainguard layers them first by scope, from broadest to most specific:

1. The organization's all-repos bindings.
1. Each folder's all-repos bindings, outer folders before nested ones.
1. The repository's own bindings.

Within each scope, bindings layer by selector kind:

1. All bindings.
1. Variant bindings.
1. Exact bindings.

Packages and runtime repositories accumulate across layers. When two layers set the same environment variable, annotation, or other single value, the more specific layer wins: exact over variant, variant over all, and the repository's own bindings over any all-repos binding.

For example, suppose a repository has the following bindings:

* An all binding whose overlay adds `curl`.
* A dev variant binding whose overlay adds `strace`.
* An exact binding on `latest-dev` whose overlay adds `gdb`.

Chainguard adds these packages to each tag:

| Tag | Packages added |
| --- | --- |
| `latest-dev` | `curl`, `strace`, `gdb` |
| Other `-dev` tags | `curl`, `strace` |
| All other tags | `curl` |

### Several bindings of the same kind

You can bind several overlays to one repository with the same kind of selector. For example, you can bind a certificates overlay and a packages overlay to a repository, both with an all selector.

Bindings of the same kind in the same scope have no precedence order, so their overlays must not contradict each other. Chainguard rejects a binding if it matches a tag that another binding of the same kind in the same scope also matches, and the two overlays set any of the following to different values:

* An environment variable
* An annotation
* A named certificate, runtime key, user, or group
* Another single value, such as the user the image runs as

Packages and runtime repositories never cause a binding conflict, because Chainguard combines them. Combined packages can still fail a build if the packages themselves are incompatible, for example if two of them install the same file. Chainguard also runs the conflict check when you update an overlay, against every repository the overlay is bound to.

Bindings in different scopes never conflict, because scopes have a precedence order. You can bind a given overlay to a repository only once.

## Version templates in package names

Many packages include a language version in their name, such as `py3.12-cryptography` and `py3.14-cryptography`. To add the right package to every tag with one overlay, use the `{{major}}` and `{{minor}}` placeholders in the package name:

```yaml
contents:
  packages:
    - py{{major}}.{{minor}}-cryptography
```

When Chainguard builds each tag, it replaces the placeholders with the major and minor version of the image's main package. The `3.12` tags of the `python` image receive `py3.12-cryptography`, and the `3.14` tags receive `py3.14-cryptography`. When a tag moves to a new version, the package follows it.

Chainguard reads each image's main package from its `dev.chainguard.package.main` label. To check the label, run the following [crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane) command:

```shell
crane config cgr.dev/$ORGANIZATION/python:3.12 | jq -r '.config.Labels["dev.chainguard.package.main"]'
```

```output
python-3.12
```

In this example, the main package is Python 3.12, so `{{major}}` becomes `3` and `{{minor}}` becomes `12`.

`{{major}}` and `{{minor}}` are the only supported placeholders. If you create or update an overlay with any other `{{...}}` placeholder, Chainguard rejects it.

If an image has no main package, or its version has no major and minor components, Chainguard can't fill in the placeholders. That tag's build fails, and the build logs name the package.

## Supported customizations

An overlay supports the same customizations as standard Custom Assembly, with the same validation rules:

* Packages (`contents.packages`)
* [Custom runtime repositories](/chainguard/containers/custom-assembly/overview/#custom-runtime-repositories) (`contents.runtime_repositories`)
* [Custom runtime keys](/chainguard/containers/custom-assembly/overview/#custom-runtime-keys) (`contents.runtime_keyring`)
* [Environment variables and annotations](/chainguard/containers/custom-assembly/custom-assembly-chainctl/#adding-custom-annotations-and-environment-variables) (`environment` and `annotations`)
* [User accounts and groups](/platform/chainctl/chainctl-docs/chainctl_images_repos_build_apply/) (`accounts`)
* [Custom certificates](/chainguard/containers/custom-assembly/custom-assembly-certs/) (`certificates.additional`)
* [Guarded Entrypoint](/chainguard/containers/custom-assembly/guarded-entrypoint/overview/) (`guarded_entrypoint`, `fail_mode`, `preflight`, and `command_override`). To learn how these fields combine when several bindings match one tag, refer to [How the fields combine across bindings](/chainguard/containers/custom-assembly/guarded-entrypoint/overview/#how-the-fields-combine-across-bindings).

Overlays don't support Chainguard-managed certificate bundles (`certificates.providers`). If an overlay contains a field that overlays don't support, Chainguard rejects the whole overlay instead of ignoring the field.

## Limitations

Custom Assembly Overlays have the following limitations:

* **One model per repository.** A repository can use standard Custom Assembly or overlays, but not both. If a repository has one kind of customization, adding the other kind fails with the error `repository custom overlay and overlay binding not allowed`. To move a repository from standard Custom Assembly to overlays, contact your Chainguard account team.
* **No Chainguard Console support.** Manage overlays and bindings with `chainctl`, Terraform, or the Chainguard API. The Console's Custom Assembly editor manages standard Custom Assembly only.
* **A missing package fails the build.** If a package in an overlay can't be installed on a tag, that tag's build fails. Chainguard doesn't skip the package. The build logs name the package that failed.
* **No removing base packages.** As with standard Custom Assembly, an overlay can add to an image but can't remove packages from the source image.

## Permissions

Overlays and bindings use their own capabilities:

* `registry.overlays.list` lets you view overlays and bindings. The built-in `viewer`, `editor`, and `owner` roles include it.
* `registry.overlays.edit` lets you create, update, and delete overlays and bindings. The built-in `editor` and `owner` roles include it.

To create a custom role with these capabilities, refer to [Overview of roles and role-bindings in Chainguard](/platform/administration/iam-organizations/roles-role-bindings/roles-role-bindings/).

## Learn more

* [Manage Custom Assembly Overlays with chainctl](/chainguard/containers/custom-assembly/overlays/chainctl/)
* [Manage Custom Assembly Overlays with Terraform](/chainguard/containers/custom-assembly/overlays/terraform/)
* [Overview of Chainguard Custom Assembly](/chainguard/containers/custom-assembly/overview/)
* [Custom Assembly FAQs](/chainguard/containers/custom-assembly/faq/)

