From 5690dd6c2d098d8f2a6cc38bbaac4c4c82a19980 Mon Sep 17 00:00:00 2001 From: Matthew Helmke Date: Thu, 12 Mar 2026 09:27:27 -0500 Subject: [PATCH 1/4] Update rules_bazel page and add to private apk repo page` --- .../packages/private-apk-repos/index.md | 66 ++- .../build-tools/apko/bazel-rules.md | 541 +++++++++++++----- 2 files changed, 464 insertions(+), 143 deletions(-) diff --git a/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md b/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md index c4a4d059c7..02b7169ed1 100644 --- a/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md +++ b/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md @@ -6,7 +6,7 @@ aliases: type: "article" description: "An overview of how to work with Chainguard's Private APK Repositories." date: 2025-02-21T11:07:52+02:00 -lastmod: 2025-02-21T11:07:52+02:00 +lastmod: 2026-03-10T08:07:52+02:00 draft: false tags: ["Chainguard Containers"] images: [] @@ -28,7 +28,7 @@ For example, say your organization has access to the [Chainguard MySQL container Chainguard's private APK repositories are available to all Chainguard Containers customers. -## Your Repository Address +## Your repository address Your private APK repository will be available at a URL like the following: @@ -105,7 +105,7 @@ apk update Following that, you can proceed to search and install packages from your private APK repository. -## Searching for and Installing Packages +## Searching for and installing packages As an example of how you can search for and install packages from these private repositories, this section will install `wget`. However, you could also try this out with any apk package that is included in any of the Chainguard container images your organization has access to. @@ -214,7 +214,7 @@ wget policy: As this output shows, the `wget` apk package is installed in the container. -## Using Private APK Repositories with Apko Builds +## Using Private APK Repositories with apko builds You can also use your private APK repository with [apko](/open-source/build-tools/apko/overview/) builds. One of the advantages of this method is that you can build distroless images that include only the apk packages you need in the final image. @@ -265,9 +265,65 @@ You'll get output similar to the following, indicating that the `wget` package w . . . ``` + +## Using Private APK Repositories with Bazel rules for apko + +You can also use your private APK repository with [Bazel](https://bazel.build/) using +[rules_apko](https://github.com/chainguard-dev/rules_apko), which wraps +`apko` for use in Bazel builds. As with the `apko` approach, you get the +advantage of building distroless images that include only the APK packages +you need in the final image, with the additional benefits of Bazel's +hermetic, reproducible, and cached build system. + +As with the previous examples, you'll need to provide the `HTTP_AUTH` +environment variable containing your Chainguard token to the `apko` runtime +building the image: + +```shell +export HTTP_AUTH="basic:apk.cgr.dev:user:$(chainctl auth token --audience apk.cgr.dev)" +``` + +In your `apko.yaml`, reference your private Chainguard APK repository: +```shell +cat > apko.yaml < -## Pull Token for Authentication in Automated Workflows +## Pull token for authentication in automated workflows Use a pull token with a custom time to live (TTL) to authenticate to your private APK repository. The following example creates a pull token with a TTL of diff --git a/content/open-source/build-tools/apko/bazel-rules.md b/content/open-source/build-tools/apko/bazel-rules.md index 628bef60d3..bb24641667 100644 --- a/content/open-source/build-tools/apko/bazel-rules.md +++ b/content/open-source/build-tools/apko/bazel-rules.md @@ -7,7 +7,7 @@ type: "article" lead: "Build secure, minimal Wolfi-based container images using Bazel" description: "Build secure, minimal Wolfi-based container images using Bazel" date: 2023-10-23T08:49:31+00:00 -lastmod: 2024-05-02T16:49:31+00:00 +lastmod: 2026-03-12T00:00:00+00:00 draft: false tags: ["apko", "Procedural",] images: [] @@ -18,235 +18,500 @@ weight: 900 toc: true --- -`rules_apko` is an open source plugin for Bazel that makes it possible to build secure, minimal Wolfi-based container images using the popular Bazel build system. This wraps the [apko](https://github.com/chainguard-dev/apko) tool for use under Bazel. +`rules_apko` is an open source plugin for Bazel that makes it possible to build +secure, minimal Wolfi-based container images using the Bazel build system. It +wraps the [apko](https://github.com/chainguard-dev/apko) tool for use under +Bazel, providing hermetic, reproducible image builds with full Bazel caching +support. + +This page covers `rules_apko` version `1.5.37` with Bazel `9.0.1` using +**Bzlmod**, which is the only supported dependency management method in Bazel 9. +If you are on an earlier version of Bazel, you should upgrade to Bazel 9 before +following this guide. + +> **Note:** You do not need to install `apko` separately. `rules_apko` manages +> its own hermetic `apko` toolchain and automatically downloads `apko v1.1.12` +> on first build. + +By the end of this guide you will have a working Bazel project that builds a +minimal Wolfi-based container image using `rules_apko`. ## Prerequisites -First, be sure you have Bazel installed, you can follow the [Bazel installation guide](https://bazel.build/install) for more details. +Before you begin, ensure you have the following: -Next, `rules_apko` requires a one-time setup to configure Bazel to be able to make partial fetches. +- **Bazel 9.x** installed. This page was written and tested against Bazel + `9.0.1`. Follow the [Bazel installation guide](https://bazel.build/install) + for details. Note the Bazel version in use by confirming the + `dev.chainguard.package.main` label on your image is `bazel-9`. +- **`chainctl`** installed and authenticated. `chainctl` is the Chainguard + command line tool. See the + [chainctl documentation](/chainguard/chainctl/) for + installation instructions. +- **`rules_apko` version `1.5.37`** available in the + [Bazel Central Registry](https://registry.bazel.build/modules/rules_apko). + No separate download is required — Bazel fetches it automatically when you + declare it in `MODULE.bazel`. -Paste the following into your root `BUILD` file. +## Project Structure -```python -load("@rules_apko//apko:defs.bzl", "apko_bazelrc") +A complete `rules_apko` project requires the following files: -apko_bazelrc() ``` +my-apko-image/ +├── .bazelrc ← create manually +├── MODULE.bazel ← create manually, updated in two stages +├── BUILD.bazel ← create manually, updated in two stages +├── apko.yaml ← create manually +├── apko.lock.json ← generated by bazel run //:lock +├── MODULE.bazel.lock ← auto-generated by Bazel, do not edit +└── .apko/ +├── .bazelrc ← generated by bazel run //:apko_bazelrc +└── range.sh ← generated by bazel run //:apko_bazelrc +``` + +The files marked "generated" should be committed to your repository after +generation. The files marked "create manually" are written by you as part of +this guide. + +## Setup -> **Note**: By default, `apko_bazelrc` will generate `.bazelrc` to accommodate for fetching from `dl-cdn.alpinelinux.org` and `packages.wolfi.dev`. this can be configured by passing the `repositories` attribute to `apko_bazelrc()` call. +### Step 1: Create Your Project Directory -Then, run the following command. +Create a new directory for your project and change into it: ```shell -bazel run @@//:apko_bazelrc && chmod +x .apko/range.sh +mkdir my-apko-image && cd my-apko-image ``` -Finally, paste this into your preferred \`.bazelrc\` file, +### Step 2: Create the Root .bazelrc File + +Create a root `.bazelrc` file in your project directory. This file does two +things: +- It activates the apko credential helpers for partial HTTP range +requests (once they are generated in a later step) +- It keeps the Bazel repository cache outside your project directory, +which is required by Bazel 9: ``` -# Required for rules_apko to make range requests -try-import %workspace%/.apko/.bazelrc +# Import apko credential helper configuration for partial package fetches +try-import .apko/.bazelrc + +# Keep Bazel repo cache outside the project directory (required by Bazel 9) +build --repo_contents_cache=/tmp/bazel-cache ``` -Review additional [initial setup documentation](https://github.com/chainguard-dev/rules_apko/blob/main/docs/initial-setup.md) updates in the `rules_apko` [repo](https://github.com/chainguard-dev/rules_apko). +> NOTE: The try-import directive tells Bazel to load `.apko/.bazelrc` if +it exists. This file is generated in a later step by running +`bazel run //:apko_bazelrc`. Until that step is complete the directive is +safely ignored. -## Installation -To install v1.0.0, you can follow one of the options below. For other releases, follow the instructions in the release notes from the release you wish to use: [https://github.com/chainguard-dev/rules_apko/releases](https://github.com/chainguard-dev/rules_apko/releases). +### Step 3: Create the Stage 1 MODULE.bazel File -### Using Bzlmod with Bazel 6 +Create a `MODULE.bazel` file in your project directory. This file declares your +project's external dependencies and sets up the `apko` toolchain. At this stage +it does not yet include the lock file translation — that is added after the lock +file is generated: -1. Enable with `common --enable_bzlmod` in `.bazelrc`. -1. Add to your `MODULE.bazel` file: +```shell +module( + name = "my-apko-image", + version = "0.0.0", +) -```starlark -bazel_dep(name = "rules_apko", version = "1.0.0-rc1") +bazel_dep(name = "rules_apko", version = "1.5.37") + +# Set up the apko toolchain. +# apko v1.1.12 is downloaded automatically — no separate installation needed. +toolchain = use_extension("@rules_apko//apko:extensions.bzl", "apko") +toolchain.toolchain(apko_version = "v1.1.12") +use_repo(toolchain, "apko_toolchains") +register_toolchains("@apko_toolchains//:all") ``` -### Using WORKSPACE -Paste this snippet into your file: +### Step 4: Create the Stage 1 BUILD.bazel File + +Create a `BUILD.bazel` file in your project directory. At this stage it contains +only the `apko_bazelrc` and `apko_lock `rules. The `apko_image` rule is added +after the lock file is generated: + +```shell +load("@rules_apko//apko:defs.bzl", "apko_bazelrc", "apko_lock") + +# Generates .apko/.bazelrc and .apko/range.sh for partial package fetches. +# Run with: bazel run //:apko_bazelrc +apko_bazelrc() -```starlark -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -http_archive( - name = "rules_apko", - sha256 = "5c91a2322bec84a0005dd8178495775938b581053812e70d21b030bef3625623", - strip_prefix = "rules_apko-1.0.0-rc1", - url = "https://github.com/chainguard-dev/rules_apko/releases/download/v1.0.0-rc1/rules_apko-v1.0.0-rc1.tar.gz", +# Generates the lock file that pins all package versions and checksums. +# Run with: bazel run //:lock +apko_lock( + name = "lock", + config = "apko.yaml", + lockfile_name = "apko.lock.json", ) +``` + +> NOTE: The reason `apko_image` is not included at this stage is that the +`apko_image` rule checks for `apko.lock.json` at Bazel load time — before any +targets are run. Since the lock file does not exist yet, including a`pko_image` +at this stage would cause a load-time error. Once the lock file exists you will +add `apko_image` in a later step. + +### Step 5: Create the apko.yaml Configuration File + +Create an `apko.yaml` file in your project directory. This file defines the +container image — its base packages, repository, signing key, and target +architectures. The following example builds a minimal Wolfi-based image: + +```yaml +contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + - wolfi-base + +entrypoint: + command: /bin/sh + +archs: + - aarch64 + - x86_64 +``` -###################### -# rules_apko setup # -###################### -# Fetches the rules_apko dependencies. -# If you want to have a different version of some dependency, -# you should fetch it *before* calling this. -# Alternatively, you can skip calling this function, so long as you've -# already fetched all the dependencies. -load("@rules_apko//apko:repositories.bzl", "apko_register_toolchains", "rules_apko_dependencies") +## Generating Configuration and the Lock File -rules_apko_dependencies() +With your project files in place, run the following two commands in order. -apko_register_toolchains(name = "apko") +### Step 6: Generate the Credential Helper Configuration -load("@rules_apko//apko:translate_lock.bzl", "translate_apko_lock") +Run the `apko_bazelrc` target to generate `.apko/.bazelrc` and `.apko/range.sh`: -translate_apko_lock( - name = "example_lock", - lock = "@//:apko.lock.json", -) +```shell +bazel run //:apko_bazelrc +``` -load("@example_lock//:repositories.bzl", "apko_repositories") +You will see output similar to the following: -apko_repositories() +```shell +INFO: Analyzed target //:apko_bazelrc (6 packages loaded, 14 targets configured). +INFO: Found 1 target... +Target //:apko_bazelrc up-to-date: + bazel-bin/apko_bazelrc_update.sh +INFO: Build completed successfully, 2 total actions +INFO: Running command line: bazel-bin/apko_bazelrc_update.sh +Copying file .../range.sh to .apko/range.sh in /home/user/my-apko-image +Copying file .../apko_bazelrc_bazelrc to .apko/.bazelrc in /home/user/my-apko-image ``` -## Rules -Public API re-exports +This generates two files in the `.apko/` subdirectory: -## apko_image +- `.apko/.bazelrc` — configures Bazel credential helpers for the Wolfi and +Alpine package repositories, enabling partial HTTP range requests so Bazel +fetches only the specific byte ranges of APK packages it needs +- `.apko/range.sh` — the credential helper script used by Bazel when making +range requests -
-apko_image(name, architecture, args, config, contents, output, tag)
-
+Both files should be committed to your repository. They are activated by the +try-import `.apko/.bazelrc` directive in your root `.bazelrc` file. -Build OCI images from APK packages directly without Dockerfile. +> NOTE: By default, `apko_bazelrc` configures credential helpers for +`dl-cdn.alpinelinux.org` and p`ackages.wolfi.dev`. If you are using additional +repositories, pass them to the repositories attribute: +`apko_bazelrc(repositories = ["my.repo.example.com"])`. -This rule creates Alpine images using the `apko.yaml` configuration file and relies on cache contents generated by [translate_lock](https://github.com/chainguard-dev/rules_apko/blob/main/docs/translate_lock.md) to be fast. -```starlark -apko_image( - name = "example", - config = "apko.yaml", - contents = "@example_lock//:contents", - tag = "example:latest", +### Step 7: Generate the Lock File + +Run the lock target to generate apko.lock.json: + +```shell +bazel run //:lock +``` + +You will see output similar to the following: + +```shell +INFO: Analyzed target //:lock (84 packages loaded, 487 targets configured). +INFO: Found 1 target... +Target //:lock up-to-date: + bazel-bin/_lock_run.sh +INFO: Build completed successfully, 1 total action +INFO: Running command line: bazel-bin/_lock_run.sh +2026/03/12 13:07:06 INFO Determining packages for 2 architectures: [arm64 amd64] +2026/03/12 13:07:06 INFO Discovered 0 auto-discovered keys +``` + +This generates `apko.lock.jso`n in your project directory. The lock file pins +the exact versions and checksums of all packages required to build your image +for each target architecture. Commit this file to your repository to ensure +reproducible builds. + +> NOTE: The `DEBUG` message `apko toolchain apko has multiple versions ["v1.1.12", "v1.1.12"], selected v1.1.12` may appear in your output. This is +normal and expected — it occurs because both the toolchain setup and the lock +translation in `MODULE.bazel` reference the same `apko` extension. It does not +indicate a problem. + +## Building the Image + +Now that the lock file exists, update your project files to add the image build +target. + +### Step 8: Update MODULE.bazel to Add Lock File Translation + +Add the translate_lock extension call to the end of your `MODULE.bazel` file: + +``` +module( + name = "my-apko-image", + version = "0.0.0", +) + +bazel_dep(name = "rules_apko", version = "1.5.37") + +# Set up the apko toolchain. +# apko v1.1.12 is downloaded automatically — no separate installation needed. +toolchain = use_extension("@rules_apko//apko:extensions.bzl", "apko") +toolchain.toolchain(apko_version = "v1.1.12") +use_repo(toolchain, "apko_toolchains") +register_toolchains("@apko_toolchains//:all") + +# Translate the lock file into Bazel repository targets. +# Add this section after apko.lock.json has been generated. +apk = use_extension("@rules_apko//apko:extensions.bzl", "apko") +apk.translate_lock( + name = "wolfi_base_lock", + lock = "//:apko.lock.json", ) +use_repo(apk, "wolfi_base_lock") ``` -The label `@example_lock//:contents` is generated by the `translate_lock` extension, which consumes an 'apko.lock.json' file. For more details, refer to the [apko cache documentation](#fetching-and-caching-contents). -An example demonstrating usage with [rules_oci](https://github.com/bazel-contrib/rules_oci): +### Step 9: Update BUILD.bazel to Add the Image Target -```starlark -apko_image( - name = "alpine_base", +Add the `apko_image` rule to your `BUILD.bazel` file: + +```shell +load("@rules_apko//apko:defs.bzl", "apko_bazelrc", "apko_image", "apko_lock") + +# Generates .apko/.bazelrc and .apko/range.sh for partial package fetches. +# Run with: bazel run //:apko_bazelrc +apko_bazelrc() + +# Generates the lock file that pins all package versions and checksums. +# Run with: bazel run //:lock +apko_lock( + name = "lock", config = "apko.yaml", - contents = "@alpine_base_lock//:contents", - tag = "alpine_base:latest", + lockfile_name = "apko.lock.json", ) -oci_image( - name = "app", - base = ":alpine_base" +# Builds the container image from the lock file contents. +# Run with: bazel build //:wolfi_base +apko_image( + name = "wolfi_base", + config = "apko.yaml", + contents = "@wolfi_base_lock//:contents", + tag = "wolfi-base:latest", ) ``` -For more examples checkout the [examples](https://github.com/chainguard-dev/rules_apko/tree/main/examples) directory. -### Attributes +The `contents` attribute references `@wolfi_base_lock//:contents` — this is the +Bazel repository generated by the `translate_lock` call in `MODULE.bazel`. The +name `wolfi_base_lock` in both files must match. + -| Name | Description | Type | Mandatory | Default | -| :------------- | :------------- | :------------- | :------------- | :------------- | -| name | A unique name for this target. | Name | required | | -| architecture | the CPU architecture which this image should be built to run on. See https://github.com/chainguard-dev/apko/blob/main/docs/apko_file.md#archs-top-level-element | String | optional | "" | -| args | additional arguments to provide when running the apko build command. | List of strings | optional | [] | -| config | Label to the apko.yaml file. | Label | required | | -| contents | Label to the contents repository generated by translate_lock. See [apko-cache](https://github.com/chainguard-dev/rules_apko/blob/main/docs/apko-cache.md) documentation. | Label | required | | -| output | - | String | optional | "oci" | -| tag | tag to apply to the resulting docker tarball. only applicable when output is docker | String | required | | +### Step 10: Build the Image -### apko_bazelrc +Run the build: -
-apko_bazelrc(name, repositories, kwargs)
-
+```shell +bazel build //:wolfi_base +``` -Helper macro for generating `.bazelrc` and `range.sh` files to allow for partial package fetches. +You will see output similar to the following: -Review [Prerequisites](#prerequisites) documentation for more information. +```shell +INFO: Analyzed target //:wolfi_base (123 packages loaded, 656 targets configured). +INFO: From Action wolfi_base: +2026/03/12 12:46:27 INFO installing wolfi-keys (1-r13) arch=aarch64 +2026/03/12 12:46:27 INFO installing wolfi-baselayout (20230201-r28) arch=aarch64 +2026/03/12 12:46:27 INFO installing wolfi-keys (1-r13) arch=x86_64 +2026/03/12 12:46:27 INFO installing wolfi-baselayout (20230201-r28) arch=x86_64 +2026/03/12 12:46:27 INFO installing ca-certificates-bundle (20251003-r4) arch=x86_64 +... +2026/03/12 12:46:27 INFO installing wolfi-base (1-r7) arch=x86_64 +2026/03/12 12:46:27 INFO layer digest: sha256:44cc053506b4e236f7e32026147836ce082fa58d0a329ff2aab1bb61d0c6bcfc arch=x86_64 +INFO: Found 1 target... +Target //:wolfi_base up-to-date: + bazel-bin/wolfi_base +INFO: Build completed successfully, 128 total actions +``` -### Parameters +The built image is available at `bazel-bin/wolfi_base`. -| Name | Description | Default Value | -| :------------- | :------------- | :------------- | -| name | name of the target | "apko_bazelrc" | -| repositories | list of repositories to generate .bazelrc for | ["dl-cdn.alpinelinux.org", "packages.wolfi.dev"] | -| kwargs | passed to expanding targets. only well known attributes such as tags testonly ought to be present. | none | +> NOTE: You may see `INFO` messages about duplicate package IDs in the SBOM +during the build, for example: +`INFO duplicate package ID found in SBOM, deduplicating package...` +These are normal and expected — `apko` deduplicates packages that appear +multiple times in the dependency graph when generating the Software Bill of +Materials (SBOM). They do not indicate a problem with your build. -## Fetching and Caching Contents +On subsequent builds, Bazel's caching means most actions will be +retrieved from cache rather than rebuilt. You will see output like +`127 action cache hit, 1 internal` — this is expected and demonstrates one +of the key benefits of building images with Bazel. -To ensure efficient operation, the `apko_image` rule must maintain a cache of remote contents that it fetches from repositories. While outside of Bazel, `apko` manages its own cache, under Bazel, the cache must be maintained by Bazel to ensure correctness and speed. Therefore, Bazel needs to know what needs to be fetched and from where to cache these HTTP requests and provide them to `apko` as required. -The `apko.lock.json` file contains all the necessary information about how to perform the HTTP fetches required by `apko` to build the container image. +## Updating the Lock File -### Generating the Lock File +When you update `apko.yaml` to add, remove, or change packages, regenerate the +lock file by running: -> **Note:** Documentation for lockfile generation [will be added to the repository docs](https://github.com/chainguard-dev/rules_apko/blob/main/docs/apko-cache.md) once the `apko resolve` command is available. +```shell +bazel run //:lock +``` -### Using `translate_lock` +Then rebuild the image: -Having just the `apko.lock.json` file alone is insufficient; all the information needs to be converted into `apk_` repository calls to make them accessible to Bazel. The `translate_lock` tool accomplishes this by taking the `apko.lock.json` file and dynamically generating the required Bazel repositories. +```shell +bazel build //:wolfi_base +``` -`translate_lock` will create a new bazel repository named after itself. This repository will also have a target named contents, which you can pass to `apko_image`: +## Rules Reference -```starlark +### `apko_image` + +Builds an OCI container image from APK packages using an apko.yaml +configuration file and a pre-generated lock file. + +```shell apko_image( - name = "lock", + name = "my_image", config = "apko.yaml", - # name of the repository is the same translate_lock! - contents = "@examples_lock//:contents", - tag = "lock:latest", + contents = "@my_image_lock//:contents", + tag = "my-image:latest", ) ``` -#### Usage with `bzlmod` +An example demonstrating usage with `rules_oci`: -```starlark -apk = use_extension("//apko:extensions.bzl", "apko") +```shell +apko_image( + name = "wolfi_base", + config = "apko.yaml", + contents = "@wolfi_base_lock//:contents", + tag = "wolfi-base:latest", +) -apk.translate_lock( - name = "examples_lock", - lock = "//path/to/lock:apko.lock.json", +oci_image( + name = "app", + base = ":wolfi_base", ) -use_repo(apk, "examples_lock") ``` -#### Usage with Workspace -```starlark -load("@rules_apko//apko:translate_lock.bzl", "translate_apko_lock") +#### Attributes + +| Name | Description | Type | Mandatory | Default | +| :--- | :--- | :--- | :--- | :--- | +| `name` | A unique name for this target. | Name | required | | +| `architecture` | The CPU architecture this image should be built for. See [apko architecture documentation](https://github.com/chainguard-dev/apko/blob/main/docs/apko_file.md#archs-top-level-element). | String | optional | `""` | +| `args` | Additional arguments to pass to the `apko build` command. | List of strings | optional | `[]` | +| `config` | Label to the `apko.yaml` configuration file. | Label | required | | +| `contents` | Label to the contents repository generated by `translate_lock`. See [Generating Configuration and the Lock File](#generating-configuration-and-the-lock-file). | Label | required | | +| `output` | Output format for the image. | String | optional | `"oci"` | +| `tag` | Tag to apply to the resulting image. Only applicable when `output` is `docker`. | String | required | | + -translate_apko_lock( - name = "example_lock", - lock = "//path/to/lock:apko.lock.json", +### `apko_lock` + +Generates a lock file that pins the exact versions and checksums of all packages +required to build your image. The lock file is written directly into your +project directory and should be committed to your repository. + +```shell +apko_lock( + name = "lock", + config = "apko.yaml", + lockfile_name = "apko.lock.json", ) +``` -load("@example_lock//:repositories.bzl", "apko_repositories") +Run with: +```shell +bazel run //:lock -apko_repositories() ``` -## Repository rules for translating apko.lock.json +#### Parameters -`translate_apko_lock` +| Name | Description | Default | +| :--- | :--- | :--- | +| `name` | Name of the target. | required | +| `config` | Label to the `apko.yaml` configuration file. | required | +| `lockfile_name` | Name of the generated lock file. | required | -
-translate_apko_lock(name, lock, repo_mapping, target_name)
-
-## Repository rule to generate starlark code from an `apko.lock.json` file. +### `apko_bazelrc` -Review [the section above](#fetching-and-caching-contents) for more information. +Generates `.apko/.bazelrc` and `.apko/range.sh` to enable partial HTTP range +requests when fetching APK packages. This significantly reduces download size by +fetching only the byte ranges of each package that Bazel needs. +```shell +apko_bazelrc() +``` + +Run with: + +```shell +bazel run //:apko_bazelrc +``` -### Attributes +The generated `.apko/.bazelrc` file configures Bazel credential helpers for the +specified repositories. Activate it by adding the following to your root +`.bazelrc`: +```shell +try-import .apko/.bazelrc +``` + +#### Parameters + +| Name | Description | Default | +| :--- | :--- | :--- | +| `name` | Name of the target. | `"apko_bazelrc"` | +| `repositories` | List of package repository hostnames to configure credential helpers for. | `["dl-cdn.alpinelinux.org", "packages.wolfi.dev"]` | +| `kwargs` | Standard Bazel attributes such as `tags` and `testonly`. | none | + + +## Using Private APK Repositories + +If you are using a private Chainguard APK repository, you need to provide your +Chainguard token to the `apko` runtime via the `HTTP_AUTH` environment variable. +Set it before running any `bazel run` or `bazel build` commands: + +```shell +export HTTP_AUTH="basic:apk.cgr.dev:user:$(chainctl auth token --audience apk.cgr.dev)" +``` + +In your apko.yaml, reference your private repository using your Chainguard +organization name: + +```yaml +contents: + repositories: + - https://apk.cgr.dev/$ORGANIZATION + packages: + - your-package +``` -| Name | Description | Type | Mandatory | Default | -| :------------- | :------------- | :------------- | :------------- | :------------- | -| name | A unique name for this repository. | Name | required | | -| lock | label to the apko.lock.json file. | Label | required | | -| repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<p>For example, an entry "@foo": "@bar" declares that, for any time this repository depends on @foo (such as a dependency on @foo//some:target, it should actually resolve that dependency within globally-declared @bar (@bar//some:target). | Dictionary: String -> String | required | | -| target_name | internal. do not use! | String | optional | "" | +Replace `$ORGANIZATION` with your Chainguard organization name. +For full details on setting up and using private APK repositories with +Chainguard, including how to configure your organization and authenticate, see +[Chainguard's Private APK Repositories](/content/chainguard/chainguard-images/features/packages/private-apk-repos/). \ No newline at end of file From 707f245e179374e78c5a4e8769287deea055c8e2 Mon Sep 17 00:00:00 2001 From: Matthew Helmke Date: Thu, 12 Mar 2026 09:45:07 -0500 Subject: [PATCH 2/4] fix formatting of project structure diagram --- content/open-source/build-tools/apko/bazel-rules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/open-source/build-tools/apko/bazel-rules.md b/content/open-source/build-tools/apko/bazel-rules.md index bb24641667..e200c5056f 100644 --- a/content/open-source/build-tools/apko/bazel-rules.md +++ b/content/open-source/build-tools/apko/bazel-rules.md @@ -66,8 +66,8 @@ my-apko-image/ ├── apko.lock.json ← generated by bazel run //:lock ├── MODULE.bazel.lock ← auto-generated by Bazel, do not edit └── .apko/ -├── .bazelrc ← generated by bazel run //:apko_bazelrc -└── range.sh ← generated by bazel run //:apko_bazelrc + ├── .bazelrc ← generated by bazel run //:apko_bazelrc + └── range.sh ← generated by bazel run //:apko_bazelrc ``` The files marked "generated" should be committed to your repository after From 0fc9cce6ed0bbbecf016ecf09cf7d5bafcd117e4 Mon Sep 17 00:00:00 2001 From: Matthew Helmke Date: Thu, 12 Mar 2026 11:17:37 -0500 Subject: [PATCH 3/4] fix headings and notes style --- .../build-tools/apko/bazel-rules.md | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/content/open-source/build-tools/apko/bazel-rules.md b/content/open-source/build-tools/apko/bazel-rules.md index e200c5056f..253c82284e 100644 --- a/content/open-source/build-tools/apko/bazel-rules.md +++ b/content/open-source/build-tools/apko/bazel-rules.md @@ -24,19 +24,22 @@ wraps the [apko](https://github.com/chainguard-dev/apko) tool for use under Bazel, providing hermetic, reproducible image builds with full Bazel caching support. +By the end of this guide you will have a working Bazel project that builds a +minimal Wolfi-based container image using `rules_apko`. + +## How to build a container with Bazel using `rules_apko` + This page covers `rules_apko` version `1.5.37` with Bazel `9.0.1` using **Bzlmod**, which is the only supported dependency management method in Bazel 9. If you are on an earlier version of Bazel, you should upgrade to Bazel 9 before following this guide. -> **Note:** You do not need to install `apko` separately. `rules_apko` manages +> Note: You do not need to install `apko` separately. `rules_apko` manages > its own hermetic `apko` toolchain and automatically downloads `apko v1.1.12` > on first build. -By the end of this guide you will have a working Bazel project that builds a -minimal Wolfi-based container image using `rules_apko`. -## Prerequisites +### Prerequisites Before you begin, ensure you have the following: @@ -53,7 +56,8 @@ Before you begin, ensure you have the following: No separate download is required — Bazel fetches it automatically when you declare it in `MODULE.bazel`. -## Project Structure + +### Project Structure A complete `rules_apko` project requires the following files: @@ -74,9 +78,10 @@ The files marked "generated" should be committed to your repository after generation. The files marked "create manually" are written by you as part of this guide. + ## Setup -### Step 1: Create Your Project Directory +### Step 1: Create your project directory Create a new directory for your project and change into it: @@ -84,7 +89,7 @@ Create a new directory for your project and change into it: mkdir my-apko-image && cd my-apko-image ``` -### Step 2: Create the Root .bazelrc File +### Step 2: Create the root .bazelrc file Create a root `.bazelrc` file in your project directory. This file does two things: @@ -101,13 +106,13 @@ try-import .apko/.bazelrc build --repo_contents_cache=/tmp/bazel-cache ``` -> NOTE: The try-import directive tells Bazel to load `.apko/.bazelrc` if +> Note: The try-import directive tells Bazel to load `.apko/.bazelrc` if it exists. This file is generated in a later step by running `bazel run //:apko_bazelrc`. Until that step is complete the directive is safely ignored. -### Step 3: Create the Stage 1 MODULE.bazel File +### Step 3: Create the Stage 1 MODULE.bazel file Create a `MODULE.bazel` file in your project directory. This file declares your project's external dependencies and sets up the `apko` toolchain. At this stage @@ -131,7 +136,7 @@ register_toolchains("@apko_toolchains//:all") ``` -### Step 4: Create the Stage 1 BUILD.bazel File +### Step 4: Create the Stage 1 BUILD.bazel file Create a `BUILD.bazel` file in your project directory. At this stage it contains only the `apko_bazelrc` and `apko_lock `rules. The `apko_image` rule is added @@ -153,13 +158,13 @@ apko_lock( ) ``` -> NOTE: The reason `apko_image` is not included at this stage is that the +> Note: The reason `apko_image` is not included at this stage is that the `apko_image` rule checks for `apko.lock.json` at Bazel load time — before any targets are run. Since the lock file does not exist yet, including a`pko_image` at this stage would cause a load-time error. Once the lock file exists you will add `apko_image` in a later step. -### Step 5: Create the apko.yaml Configuration File +### Step 5: Create the apko.yaml configuration file Create an `apko.yaml` file in your project directory. This file defines the container image — its base packages, repository, signing key, and target @@ -182,11 +187,11 @@ archs: - x86_64 ``` -## Generating Configuration and the Lock File +## Generating configuration and the lock file With your project files in place, run the following two commands in order. -### Step 6: Generate the Credential Helper Configuration +### Step 6: Generate the credential helper configuration Run the `apko_bazelrc` target to generate `.apko/.bazelrc` and `.apko/range.sh`: @@ -218,13 +223,13 @@ range requests Both files should be committed to your repository. They are activated by the try-import `.apko/.bazelrc` directive in your root `.bazelrc` file. -> NOTE: By default, `apko_bazelrc` configures credential helpers for +> Note: By default, `apko_bazelrc` configures credential helpers for `dl-cdn.alpinelinux.org` and p`ackages.wolfi.dev`. If you are using additional repositories, pass them to the repositories attribute: `apko_bazelrc(repositories = ["my.repo.example.com"])`. -### Step 7: Generate the Lock File +### Step 7: Generate the lock file Run the lock target to generate apko.lock.json: @@ -250,17 +255,17 @@ the exact versions and checksums of all packages required to build your image for each target architecture. Commit this file to your repository to ensure reproducible builds. -> NOTE: The `DEBUG` message `apko toolchain apko has multiple versions ["v1.1.12", "v1.1.12"], selected v1.1.12` may appear in your output. This is +> Note: The `DEBUG` message `apko toolchain apko has multiple versions ["v1.1.12", "v1.1.12"], selected v1.1.12` may appear in your output. This is normal and expected — it occurs because both the toolchain setup and the lock translation in `MODULE.bazel` reference the same `apko` extension. It does not indicate a problem. -## Building the Image +## Building the image Now that the lock file exists, update your project files to add the image build target. -### Step 8: Update MODULE.bazel to Add Lock File Translation +### Step 8: Update MODULE.bazel to add lock file translation Add the translate_lock extension call to the end of your `MODULE.bazel` file: @@ -290,7 +295,7 @@ use_repo(apk, "wolfi_base_lock") ``` -### Step 9: Update BUILD.bazel to Add the Image Target +### Step 9: Update BUILD.bazel to add the image target Add the `apko_image` rule to your `BUILD.bazel` file: @@ -354,7 +359,7 @@ INFO: Build completed successfully, 128 total actions The built image is available at `bazel-bin/wolfi_base`. -> NOTE: You may see `INFO` messages about duplicate package IDs in the SBOM +> Note: You may see `INFO` messages about duplicate package IDs in the SBOM during the build, for example: `INFO duplicate package ID found in SBOM, deduplicating package...` These are normal and expected — `apko` deduplicates packages that appear From fcbe80ae5e64079892648d3c358f13dbd2ff03fe Mon Sep 17 00:00:00 2001 From: Matthew Helmke Date: Thu, 12 Mar 2026 12:21:04 -0500 Subject: [PATCH 4/4] edits from review --- .../features/packages/private-apk-repos/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md b/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md index 02b7169ed1..4f99c9eaed 100644 --- a/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md +++ b/content/chainguard/chainguard-images/features/packages/private-apk-repos/index.md @@ -270,7 +270,7 @@ You'll get output similar to the following, indicating that the `wget` package w You can also use your private APK repository with [Bazel](https://bazel.build/) using [rules_apko](https://github.com/chainguard-dev/rules_apko), which wraps -`apko` for use in Bazel builds. As with the `apko` approach, you get the +`apko` for use in Bazel builds. Like the `apko` approach, you get the advantage of building distroless images that include only the APK packages you need in the final image, with the additional benefits of Bazel's hermetic, reproducible, and cached build system. @@ -307,7 +307,7 @@ build //:my_image You'll get output similar to the following, indicating that the `wget` package was installed using the private APK repository: -```shell +```output INFO: Analyzed target //:my_image (123 packages loaded, 656 targets configured). INFO: From Action my_image: 2026/03/12 12:46:27 INFO installing wolfi-baselayout (20230201-r28) arch=x86_64