# How to pull packages from Chainguard package repositories through Artifactory

URL: https://chainguard-docs-preview-git-1016-404s.chainguard.app/chainguard/containers/registry/pull-through-guides/artifactory-packages-pull-through.md
Last Modified: September 28, 2026
Tags: Chainguard Containers, Procedural

Tutorial for setting up remote Artifactory repositories as pull-through caches for apk packages from Chainguard's package repositories.


This tutorial details how to set up remote Alpine package (apk) repositories with [JFrog Artifactory](https://jfrog.com/artifactory/), which can provide pull-through caches for Chainguard package repositories. Specifically, this guide walks you through how to set up remote Artifactory repositories to serve as pull-through caches for a [Chainguard private APK repository](/chainguard/containers/building-and-modifying/packages/private-apk-repos/) as well as Chainguard's public package repositories. The guide also outlines how to configure a container image build to pull APK packages from these remote repositories using tokens generated by Artifactory.

## Prerequisites

In order to complete this tutorial, you need the following:

* Administrative privileges over an Artifactory instance.
* [`chainctl`](/platform/chainctl-usage/how-to-install-chainctl/)
* Administrative privileges within your Chainguard organization to create role-bindings (`role_bindings.create`); this capability is available to users with [the `owner` role](/platform/administration/iam-organizations/roles-role-bindings/capabilities-reference/#chainguard-role-capabilities).

## Creating a Chainguard pull token for remote repository authentication

When configuring an Artifactory remote repository to function as a pull-through cache for packages from a Chainguard private APK repository, the remote repository must authenticate to Chainguard. This section outlines the steps necessary to create a Chainguard pull token and configure the required permissions to access your Chainguard organization's private APK repository:

Generate a pull token:

```shell
chainctl auth pull-token --repository=apk -o env
```

This `chainctl` command's `--repository=apk` flag creates a role binding to bind the pull token identity the `apk.pull` role, enabling the identity to download packages from the private APK repository of the parent organization.

This command's output shows the pull token credentials:

```output
Creating new APK registry pull-token in example.org

To use this pull token in another environment, supply the following for Basic authorization:

Username: <identity-id>

Password: <pull-token>
```

Be sure to note down both the `Username` and `Password` values returned by this command, as you will need these when setting up a remote repository on Artifactory.

## Setting up a pull-through cache for a private APK repository

Now that you have an identity with permissions to do so, you can set up a remote Artifactory repository to reach your private APK repository.

To set up the remote repository:

1. Log in to the JFrog platform.
2. Select the **Administration** tab near the top of the screen.
3. Select **Repositories** from the left-hand navigation menu.
4. On the Repositories page, click the **Create a Repository** button.
5. Select the **Remote** option.
6. In the **Select Package Type** window, select **Alpine**.

This takes you to a **Basic** configuration tab where you can enter the following details for the remote repository:

* **Repository Key** — This is a name used to identify your remote repository, for example `cg-private`.
* **URL** — This must be set to `https://apk.cgr.dev/<organization>`, replacing `<organization>` with your organization's name as it appears in the Chainguard Console. For example, if your organization is named `example` use `https://apk.cgr.dev/example`.
* **User Name** — This is used by Artifactory to authenticate to Chainguard and access your private APK repository. Use the pull token `Username` value you generated with `chainctl` in the previous step.
* **Password / Access Token** — This is used along with the user name to authenticate to Chainguard. Here, enter the `Password` value returned by the `chainctl auth pull-token` command in the previous section.

Then, navigate to the **Advanced** configuration tab and enter the following details:

* **Disable URL Normalization** — This must be enabled because, in some cases, the normalization will invalidate the URLs that packages are served from.

Click the **Create Remote Repository** button to create the remote repository and return to the **Repositories** page.

> **Note**: For more information on how to set up a remote repository in Artifactory, refer to [the official documentation](https://jfrog.com/help/r/jfrog-artifactory-documentation/remote-repositories).

### Generating a token for the remote repository

Before testing whether you're able to pull packages through the remote Artifactory repository, you must retrieve a token generated for it by Artifactory.

1. On the Repositories page, find your repository.
2. Hover your cursor over the repository's row and click the ellipsis (**⋯**) all the way to the right.
3. Select **Set me up**.
4. Click the **Generate Token & Create Instructions** button.
5. Copy or take note of the token generated for your repository.

With this token in hand, run the following `export` command to create an environment variable, replacing `my-token` with the token you noted down. You will use this environment variable in the following section as you test out the remote repository:

```shell
export CG_PRIVATE_TOKEN=my-token
```

Additionally, create two more environment variables to hold your Artifactory username and server name:

```shell
export ARTIFACTORY_USERNAME=my-user-profile
export ARTIFACTORY_SERVER_NAME=my-artifactory-hostname
```

If you aren't sure of these values, you can find them in the command from the **Set Up An Alpine Client** window where you retrieved the token:

```shell
sudo sh -c "echo 'https://linky:<TOKEN>@example-server-name.jfrog.io/artifactory/cg-private/<BRANCH>/<REPOSITORY>'" >> /etc/apk/repositories
```

In this example, the Artifactory username is `linky` and the hostname is `example-server-name`.

> **Note**: If your Artifactory username is an email address, you must percent-encode the `@` sign, as in `export ARTIFACTORY_USERNAME=linky%40example.com`. Here, the Artifactory username is `linky@example.com`, but it must be entered into the environment variable as `linky%40example.com`.

### Testing pull-through from private APK repository

This section outlines how to build a container image using a Chainguard image as a base and configure it to pull packages from the private APK repositories through your remote Artifactory repository.

Open a terminal and create a Dockerfile. Note the use of single quotes around `'EOF'` to prevent the shell from expanding the `$` variables — these will be processed by Docker during the build:

```shell
cat > Dockerfile <<'EOF'
FROM cgr.dev/chainguard/python:latest-dev
USER root
ARG ARTIFACTORY_USERNAME
ARG ARTIFACTORY_SERVER_NAME
RUN --mount=type=secret,id=cg_private_token \
    cp /etc/apk/repositories /etc/apk/repositories.disabled && \
    echo "https://${ARTIFACTORY_USERNAME}:$(cat /run/secrets/cg_private_token)@${ARTIFACTORY_SERVER_NAME}.jfrog.io/artifactory/cg-private/" > /etc/apk/repositories && \
    apk add sed && \
    rm /etc/apk/repositories
USER nonroot
EOF
```

This Dockerfile uses the `python:latest-dev` image. You don't have to use this particular image, but because we are using the `apk` command to install a package from `cgr-private` in this Dockerfile, you should use a Chainguard container image that has this package manager available.

Note that this Dockerfile combines the repository configuration, package installation, and cleanup into a single `RUN` instruction. This is important because it ensures the Artifactory token is never stored in an image layer. The `--mount=type=secret` option mounts the token only for the duration of this `RUN` instruction and it is not written to the image or its history. The final `rm` removes the `/etc/apk/repositories` file, which contained the token in plain text for the duration of the step.

Additionally, be aware that there are limitations to what packages are available from your organization's private APK repository. For instance, your organization may not have access to the `sed` package. Refer to our [private APK repository documentation](/chainguard/containers/building-and-modifying/packages/private-apk-repos/#about-private-apk-repositories) for more information.

After creating the Dockerfile, build the image. Here, we tag the image `ar-build` and pass the Artifactory token as a Docker build secret along with the username and server name as build arguments:

```shell
docker build \
  --secret id=cg_private_token,env=CG_PRIVATE_TOKEN \
  --build-arg ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME \
  --build-arg ARTIFACTORY_SERVER_NAME=$ARTIFACTORY_SERVER_NAME \
  -t ar-build .
```

The `--secret` flag passes `CG_PRIVATE_TOKEN` as a build secret that is accessible only during the build and is never stored in the image or its history.

This command's output will show that the `sed` package was installed as expected:

```output
. . .
 => [4/4] RUN apk add sed                                                                2.8s
. . .
```

You can confirm that this package was pulled through Artifactory with the [**Artifact Repository Browser**](https://jfrog.com/help/r/jfrog-artifactory-documentation/browsing-artifacts) in the Artifactory dashboard:

1. Navigate to the **Platform** tab.
2. Find **Artifactory** in the left-hand navigation menu.
3. Select **Artifacts**.
4. Find and expand the menu option for the `cg-private` Artifactory repository.

There, you will find the `sed` package listed, along with any other packages you've pulled through the remote repository.

## Configuring pull-through caches for Chainguard's public repositories

You also have access to the public `chainguard` and `extra-packages` repositories. Because these repositories are public, they do not require authentication. To set up a pull-through cache for these package repositories on Artifactory, you can follow the same procedure outlined previously for the private APK repository.

You must create two remote repositories within Artifactory — one for each public package repo — by following the same steps as before:

1. On the Repositories page, click the **Create a Repository** button.
2. Select the **Remote** option.
3. In the **Select Package Type** window, select **Alpine**.

Enter the following details for the two remote repositories:

* **Repository Key** — This is the name used to identify your remote repository. Again, you can choose whatever names you like here but this guide's examples use the names `cg-chainguard` and `cg-extras`.
* **URL** — This must be set to `https://virtualapk.cgr.dev/<ORGANIZATION-ID>/chainguard` for the `chainguard` repository and `https://virtualapk.cgr.dev/<ORGANIZATION-ID>/extra-packages` for the `extra-packages` repository.
    * For both of these URLs, you need to replace the `<ORGANIZATION-ID>` placeholder with your Chainguard organization's UID. You can find this by running the `chainctl iam organizations list -o table`; the UID is the value in your organization's `ID` column. Alternatively, you can find it by checking the **Settings** ⇒ **General** page in the [Chainguard Console](https://console.chainguard.dev).

You **do not** need to set any values for the **User Name** or **Password / Access Token** fields, as the public repositories do not require authentication.

Next, for both repositories, navigate to the **Advanced** configuration tab and check the **Disable URL Normalization** option. This will prevent URL normalization from invalidating the URLs that packages are served from.

Keep all the remaining fields set to their default values and click the **Create Remote Repository** button. This creates the remote repository and returns you to the **Repositories** page.

After creating both repositories, generate and retrieve a token for each one as you did for the `cg-private` remote repository:

1. On the Repositories page, find both remote repositories you just created.
2. Click the ellipsis (**⋯**) all the way to the right of their respective rows.
3. Select **Set me up**.
4. Click the **Generate Token & Create Instructions** button.
5. Copy or take note of the tokens generated for your repositories.

Next, copy the resulting tokens to create a pair of environment variables. For the `cg-chainguard` repository's token:

```shell
export CG_TOKEN=<cg-chainguard-token>
```

Then for the `cg-extras` repo's token:

```shell
export EXTRAS_TOKEN=<cg-extras-token>
```

Following that, you can create a Dockerfile for the public repositories. As before, use single quotes around `'EOF'` to prevent shell expansion:

```shell
cat > Dockerfile.repos <<'EOF'
FROM cgr.dev/chainguard/python:latest-dev
USER root
ARG ARTIFACTORY_USERNAME
ARG ARTIFACTORY_SERVER_NAME
RUN --mount=type=secret,id=cg_token \
    --mount=type=secret,id=extras_token \
    cp /etc/apk/repositories /etc/apk/repositories.disabled && \
    echo "https://${ARTIFACTORY_USERNAME}:$(cat /run/secrets/cg_token)@${ARTIFACTORY_SERVER_NAME}.jfrog.io/artifactory/cg-chainguard/" > /etc/apk/repositories && \
    echo "https://${ARTIFACTORY_USERNAME}:$(cat /run/secrets/extras_token)@${ARTIFACTORY_SERVER_NAME}.jfrog.io/artifactory/cg-extras/" >> /etc/apk/repositories && \
    apk add c-ares && \
    rm /etc/apk/repositories
EOF
```

Following that, build the image, passing each Artifactory token as a Docker build secret:

```shell
docker build \
  --secret id=cg_token,env=CG_TOKEN \
  --secret id=extras_token,env=EXTRAS_TOKEN \
  --build-arg ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME \
  --build-arg ARTIFACTORY_SERVER_NAME=$ARTIFACTORY_SERVER_NAME \
  -t ar-repos -f Dockerfile.repos .
```

As this command runs, it installs the `c-ares` package into the image.

## Debugging pull-through from Chainguard’s package repositories to Artifactory

If you run into issues when trying to pull from Chainguard's package repositories through Artifactory, you can try checking for these common pitfalls:

* You may run into issues if your Artifactory username is an email address; specifically, the `@` sign can lead to errors. Be sure that you're using a user profile with a name that only contains letters and numbers. If you must use a profile with an email address for a name, try percent-encoding the `@` sign by replacing it with `%40`.
* Ensure that all [network requirements](/chainguard/containers/registry/network-requirements/) are met.
* When configuring a remote Artifactory repository, ensure that the **URL** field is set correctly.
    * If necessary, ensure that you've set the correct UID for your organization in the URL field.
* It may help to [clear the Artifactory cache](https://jfrog.com/help/r/artifactory-cleanup-best-practices/clearing-an-oversized-cache).
* It could be that your Artifactory repository was misconfigured. In this case, create and configure a new Remote Artifactory repository to test with.
* If your output returns `package mentioned in index not found`, it usually means Artifactory or a CDN is normalizing or rewriting APK URLs (often by stripping query strings, collapsing path segments, or altering tokens). To prevent this, ensure that Artifactory's **Disable URL Normalization** option is checked in order to preserve exact filenames and tokens.

## Learn more

If you haven't already done so, you may find it useful to review our [Registry overview](/chainguard/containers/registry/overview/) to learn more about Chainguard's registry. You can also learn more about Chainguard Containers by referring to our [documentation](/chainguard/containers/overview/), and learn more about working with the Chainguard platform by reviewing our [Administration documentation](/platform/administration/). If you'd like to learn more about JFrog Artifactory, we encourage you to refer to the [official Artifactory documentation](https://jfrog.com/help/r/jfrog-artifactory-documentation).

