For the complete documentation index, see llms.txt.

Policies

Configure and enforce policies that control which Chainguard image and artifact versions your organization can pull, using chainctl or the Chainguard Console
  7 min read

Policies enable you to filter and restrict Chainguard artifact updates. You do this by defining policies that control and restrict versions that will be pulled from Chainguard.

Note: Policies is in beta. Contact your Chainguard account team to enable it for your organization.

Definitions

This is how policies uses the following terms.

  • Policy — A reusable rule that determines whether an image is allowed. Each policy has a name, a description, and the resource types it applies to. Policies apply to registry repositories.
  • Binding — A link between a policy and an organization. While a binding exists, the policy is active for image pulls under that organization. Without a binding, the policy has no effect.
  • Mode — A binding’s mode controls what happens when the policy denies an image:
    • ENFORCE — Block the pull.
    • DRY_RUN — Allow the pull but record the violation.

The default mode for new bindings is DRY_RUN.

Usage

Policies are managed using chainctl. System policies are shipped with the platform.

See which policies are available to your organization:

chainctl policies list --parent=$ORGANIZATION

Inspect a policy to see its full definition and configurable parameters before enabling it:

chainctl policies describe --policy=$POLICY --parent=$ORGANIZATION

See which policies are currently active:

chainctl policies binding list --parent=$ORGANIZATION

Activate a policy in DRY_RUN mode. This example activates the “no end-of-life” artifacts policy. Chainguard recommends that you roll out policies using DRY_RUN mode first and track for a time to be certain it has the impact you intend before moving to ENFORCE.

chainctl policies enable --policy=no-eol --mode=DRY_RUN --parent=$ORGANIZATION

Some policies accept parameters. Use --param=KEY=VALUE to supply them:

chainctl policies enable --policy=cooldown --mode=DRY_RUN --param=days=7 --parent=$ORGANIZATION

Promote a policy to ENFORCE:

chainctl policies enable --policy=no-eol --mode=ENFORCE --parent=$ORGANIZATION

Disable a policy:

chainctl policies disable --policy=no-eol --parent=$ORGANIZATION

Checking whether an image is allowed

Before you depend on a specific image (pinning it in a deployment, promoting it through an environment, or adding it to a base image), you often want to know whether your active policies will let it through. chainctl policies check answers that for one image on demand, without waiting for a pull to happen.

Pass a full image reference, by tag or by digest:

chainctl policies check cgr.dev/$ORGANIZATION/python:latest

The reference identifies the organization and repository; a tag is resolved to its current digest, and a digest is used as-is. The image is then evaluated against every policy active for that organization, and each result is printed:

  POLICY  |  MODE   | PARAMETERS | RESULT
----------|---------|------------|---------
 cooldown | DRY_RUN | days=7     | DENIED
 no-eol   | ENFORCE |            | ALLOWED

Read the table per policy. The image is blocked at pull time only when a policy in ENFORCE mode returns DENIED. A DENIED from a DRY_RUN policy is reported here but would not block a real pull; it is a preview of what enforcing that policy would do. If no policies apply to the image, the command says so rather than printing a table.

Because the check evaluates against the policies you have enabled right now, it reflects your current configuration, not a historical record. To review outcomes from pulls that already happened, use policy decisions instead.

check is also built for automation. Its exit status is non-zero whenever any policy returns DENIED or ERROR, regardless of that policy’s mode, so you can gate a CI pipeline on it:

chainctl policies check cgr.dev/$ORGANIZATION/python@sha256:abc... || echo "image violates a policy"

Note that a DRY_RUN denial also produces a non-zero exit, so a passing check is stricter than what enforcement alone would block, which is useful as an early warning while you stage policies.

Policy decisions

Every time an image is pulled and evaluated against an active policy, the platform records the outcome as a decision. A decision is the result of evaluating a single image digest against a single policy at pull time. It captures the policy, the digest, the mode the policy ran under (ENFORCE or DRY_RUN), the outcome (ALLOWED, DENIED, or ERROR), a reason when one is available, and the day the pull happened.

Decisions are recorded for all evaluations, regardless of mode or outcome. Together they form an audit log of what your policies did against real pull traffic. Use them to answer questions like “why was this image blocked?”, “what has this policy decided over the past month?”, and “what would this policy block if I enforced it?”.

This is distinct from chainctl policies check, which evaluates one image against your active policies on demand. Decisions are the historical record of evaluations that already happened during real pulls.

List the decisions recorded for your organization:

chainctl policies decision list --parent=$ORGANIZATION
 REPOSITORY |     DIGEST     |  POLICY  |   MODE   | RESULT  | PULLED ON
------------|----------------|----------|----------|---------|------------
 nginx      | sha256:1a2b3c… | cooldown | DRY_RUN  | DENIED  | 2026-06-28
 nginx      | sha256:4d5e6f… | no-eol   | ENFORCED | DENIED  | 2026-06-28
 bash       | sha256:7a8b9c… | no-eol   | ENFORCED | ALLOWED | 2026-06-27

Narrow the results with filters. To see only the pulls a policy denied, filter by outcome:

chainctl policies decision list --parent=$ORGANIZATION --result=DENIED

You can also restrict to a single policy, a single mode (ENFORCE or DRY_RUN), a time window, or a single image, and request JSON for further processing. The --since flag takes a number of days with a d suffix (for example 7d), and --repo accepts an image as REPO or REPO:TAG:

chainctl policies decision list --parent=$ORGANIZATION --policy=no-eol --mode=ENFORCE --since=30d -o json
chainctl policies decision list --parent=$ORGANIZATION --repo=nginx:latest

Decisions are deduplicated per day, so repeated pulls of the same digest under the same policy and outcome appear once for that day rather than once per pull.

Example: staging a policy with dry run

DRY_RUN decisions let you measure a policy’s impact before it can block anything. Because a DRY_RUN binding records outcomes without blocking pulls, you can enable a policy, watch what it would have denied against your real traffic, and only promote it to ENFORCE once the results match your expectations.

First, enable the policy in DRY_RUN mode:

chainctl policies enable --policy=no-eol --mode=DRY_RUN --parent=$ORGANIZATION

Let your normal pull traffic flow for a representative period. Then review what the policy denied while in dry run. Use --since to limit the review to a recent window, here the last seven days:

chainctl policies decision list --parent=$ORGANIZATION --policy=no-eol --mode=DRY_RUN --result=DENIED --since=7d

Each DENIED row is a pull that would have been blocked under ENFORCE. Inspect the digests to confirm the policy is catching what you intend and nothing critical to your workloads. If the results look wrong, adjust the policy’s parameters or disable it; if they look right, promote the binding to enforcement:

chainctl policies enable --policy=no-eol --mode=ENFORCE --parent=$ORGANIZATION

After promoting, keep reviewing decisions to confirm enforcement behaves as expected. The same command now returns ENFORCE-mode rows for the pulls the policy is actively blocking.

Granting an exception with an override

Once a policy is enforcing, it blocks every image that violates it. Sometimes you need to let one specific image through anyway: a known-good build that trips a policy, an artifact approved through a separate review, or an urgent fix that cannot wait for the policy itself to change. An override is an admin-granted waiver that flips a DENIED result to ALLOWED for one policy and one image, without disabling the policy or changing its binding. Every other image stays subject to the policy.

An override is deliberate and attributable. It applies to exactly one policy and one image digest, requires a reason, and records who created it and when. The engine applies it after evaluation, so the recorded decision still reflects the policy’s verdict while the override is what ultimately allows the pull.

Creating an override requires the policies.override.create capability, which is typically held by organization owners.

Waive a policy for a specific image, identified by digest, with a reason:

chainctl policies override create \
  --policy=no-eol \
  --digest=sha256:abc123... \
  --reason="approved exception, ticket OPS-42" \
  --parent=$ORGANIZATION

The --digest value must be a manifest digest rather than a tag, so the waiver targets one exact artifact. A given policy and image can carry at most one override; to change an existing one, delete it and create it again.

Review the active overrides for an organization to see what has been waived, by whom, and why:

chainctl policies override list --parent=$ORGANIZATION
 OVERRIDE ID  | POLICY |       TARGET        |           REASON           |    CREATED BY     |       CREATED AT
--------------|--------|---------------------|----------------------------|-------------------|-------------------------
 <id>         | no-eol | image sha256:abc12… | approved exception, OPS-42 | alice@example.com | 2026-06-28 14:30:00 UTC

When the exception is no longer needed, delete the override to restore the policy’s result for that image. Use chainctl policies override list to find the ID:

chainctl policies override delete <override-id>

Once deleted, the image is again subject to whatever the policy decides.

See chainctl policies --help or the chainctl reference pages for more information.

Last updated: 2026-05-21 08:48