---
component: artifact-cache
version: "1.9"
slug: artifact-cache/configure-github-actions
canonical_url: "https://docs.develocity.ai/artifact-cache/1.9/how-to/configure-github-actions/"
title: "Configuring GitHub Actions"
description: "Configure the Develocity Artifact Cache CLI in GitHub Actions to cache Maven, Gradle, npm, pip, SonarScanner, and Docker content and Setup Cache artifacts."
keywords:
  - "GitHub Actions"
  - "Artifact Cache"
status: current
---

<!-- llms-index: https://docs.develocity.ai/llms.txt -->

# Configuring GitHub Actions

<a id="preamble"></a>

This guide shows you how to integrate the Artifact Cache CLI into your GitHub Actions workflows.

There are two supported ways to do this:

*   The **Develocity Artifact Cache GitHub Action** ([repository](https://github.com/gradle/develocity-artifact-cache-github-action)) is the officially supported and recommended path. It wraps the Artifact Cache CLI, restores the cache before your build, and stores it afterward, with a single step.
    
*   The **manual CLI setup** downloads and drives the CLI yourself. Use it on air-gapped or restricted-network runners, when your organization does not permit third-party GitHub Actions, or when you need control beyond the Action’s inputs.
    

<a id="prerequisites"></a>

## Prerequisites

Before you begin, ensure you have:

*   Develocity Edge node (version 2.3.0+) deployed
    
*   Develocity server (version 2026.3.0+)
    
*   Develocity access key with appropriate permissions
    
*   GitHub Actions runner (version 2.327.1+) if you use self-hosted runners, for the Node.js 24 actions in the examples
    
*   Access to the Artifact Cache CLI binary, [served by an Edge node or hosted in your internal network](https://docs.develocity.ai/artifact-cache/1.9/download-the-cli/)
    

See [System Requirements](https://docs.develocity.ai/artifact-cache/1.9/reference/requirements/) for complete details.

<a id="disabling-conflicting-cache-actions"></a>

## Disabling Conflicting Cache Actions

The Artifact Cache provides complete caching functionality. Disable caching in these GitHub Actions to prevent conflicts:

**setup-gradle (`gradle/actions/setup-gradle`)** — Apply it **before** the Artifact Cache Action and [disable its Gradle User Home caching](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#disabling-caching). With `gradle/actions` 6.4.0 or newer, set `cache-provider: external`, which keeps the job summary accurate by reporting that caching is handled externally rather than turned off; on older versions, set `cache-disabled: true`. Ordering matters: `setup-gradle` establishes the Gradle User Home and exports `GRADLE_USER_HOME`, which the Artifact Cache CLI auto-detects, so running it first ensures the cache is restored into and stored from the Gradle User Home your build uses. If you do not use `setup-gradle`, the CLI falls back to the default Gradle User Home (`~/.gradle`) that `./gradlew` uses, so the basic template needs no extra configuration; this coordination matters only when a custom `GRADLE_USER_HOME` is set.

**Setup Java (`actions/setup-java`)** — [Don’t enable caching](https://github.com/actions/setup-java#caching-packages-dependencies) - omit the `cache` parameter.

**Cache Action (`actions/cache`)** — Don’t use for Gradle or Maven dependencies. See [examples](https://github.com/actions/cache/blob/main/examples.md#java---gradle) for what to avoid.

<a id="using-the-github-action"></a>

## Using the GitHub Action

The `gradle/develocity-artifact-cache-github-action` Action is the officially supported way to run the Artifact Cache CLI in GitHub Actions. It restores the cache in its main step, runs your build, and stores the cache in a post step after a successful build, so you do not write the restore and store steps yourself. By default it downloads the CLI from the public Develocity host, so you do not need to host the binary to get started. If you are upgrading from version 1.0.0, see [Upgrade Notes](#upgrade-notes).

When the endpoint you set (`develocity-url` or `develocity-edge-url`) addresses a Develocity Edge node running version 2.3.0 or later, the Action downloads the CLI from that Edge node instead, using the short-lived token it already exchanged for the cache restore as the bearer credential. An explicit `cli-repository` value always wins over the Edge node, so set it to skip this detection and download from your own mirror instead (see [Air-Gapped and Custom CLI Hosting](#air-gapped)).

The Action targets one Develocity endpoint: set `develocity-url` to reach a Develocity server (passed to the CLI as `--dv-server`), or `develocity-edge-url` to target a Develocity Edge directly (`--dv-edge`). Provide exactly one (they are mutually exclusive), and whichever you set also identifies the host for the access key and short-lived-token exchange. The Action sends no credential to an `http` endpoint unless `develocity-url-allow-insecure` is `true`. Without that input, it warns and skips all cache activity.

The Action runs the Artifact Cache CLI under Java 21 (a minimum floor). On GitHub-hosted runners it resolves a suitable JDK automatically from the pre-set `JAVA_HOME_*` variables, so no `setup-java` step is required for the Action itself. It is supported on GitHub-hosted Linux, macOS, and Windows runners, and on self-hosted Linux runners. The examples below add `actions/setup-java` to provide the JDK your build runs on.

<a id="storing-the-access-key"></a>

### Storing the Access Key

In your GitHub repository settings, navigate to **Settings > Secrets and variables > Actions** and add a `DEVELOCITY_ACCESS_KEY` secret.

The `develocity-access-key` input takes the key in `<hostname>=<key_value>` form, matching the `DEVELOCITY_ACCESS_KEY` format used by the DV build agents and `setup-gradle`; a bare key value is not accepted. The Action uses the entry for the chosen endpoint host (whichever of `develocity-url` or `develocity-edge-url` is set). It is the only key source: the Action does not read the `DEVELOCITY_ACCESS_KEY` environment variable or local key files.

The Action exchanges this long-lived key for a short-lived Develocity token. It passes the token to the Artifact Cache CLI in an environment variable that only the CLI process sees. The token authenticates the restore and the store, and the CLI download when the Action gets the CLI from a Develocity Edge node. The Action does not set `DEVELOCITY_ACCESS_KEY` for other steps, so the rest of the workflow sees neither the key nor the token. A step that needs Develocity for something else, such as publishing a Build Scan, needs its own token. `gradle/actions/setup-gradle` performs the same exchange and exports the token for Gradle builds. The post step exchanges the key again for the store, so the job’s state never holds a token. Each token lasts two hours by default.

The resolved key is masked in the workflow log. Prefer an access key scoped to only the servers and permissions it needs.

<a id="oidc-authentication"></a>

### Authenticating With an OIDC Token

Requires Develocity 2026.3 or later, with a workload identity rule configured for GitHub Actions. See [Workload Identity](https://docs.develocity.ai/2026.3/administration/access-control/workload-identity/).

As an alternative to a long-lived `DEVELOCITY_ACCESS_KEY` secret, the job can authenticate with a short-lived OIDC token from GitHub’s OIDC provider (see the [GitHub Actions OpenID Connect documentation](https://docs.github.com/en/actions/concepts/security/openid-connect)). Develocity validates the token against the issuer, audience, and claim requirements configured on the matching workload identity rule. The issuer is `https://token.actions.githubusercontent.com` on GitHub.com, and `https://«hostname»/_services/token/actions` on GitHub Enterprise Server. Set the rule’s audience to the origin of the URL the Action targets, such as `https://develocity.example.com`, unless you override it with `develocity-oidc-audience` as described below. When you use an OIDC token, you no longer need to store an access key as a GitHub Actions secret.

The Action requests the OIDC token itself. Omit `develocity-access-key` and grant the job the `id-token: write` permission. Without that permission, GitHub offers the job no OIDC token, and the Action warns and skips all cache activity. Declaring `permissions` replaces the defaults, so also list the ones the job already relies on, such as `contents: read` for `actions/checkout`.

For workload identity, set `develocity-edge-url` instead of `develocity-url`. With `develocity-url`, the CLI first looks up the server’s Edge, and that lookup rejects a workload identity token. Develocity 2026.1 and later has a built-in Edge, so the Develocity server’s own URL works as `develocity-edge-url`:

```yaml
name: Artifact Cache with OIDC
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Develocity Artifact Cache
        uses: gradle/develocity-artifact-cache-github-action@v1
        with:
          develocity-edge-url: https://develocity.example.com
```

By default, the token’s audience is the origin of the endpoint, with no path or trailing slash: `https://develocity.example.com` in the example above. It must match the audience on the workload identity rule exactly. A non-default port stays in the audience, so the endpoint `https://develocity.example.com:8443/` gives the audience `https://develocity.example.com:8443`. If the rule uses a different audience, set `develocity-oidc-audience`.

When `develocity-access-key` is unset or blank, the Action uses workload identity. Blank counts as unset because GitHub passes a missing secret as an empty string. When the key is set, the Action uses it and ignores `develocity-oidc-audience`. The workflow log names the credential the Action used, so you can spot a misspelled secret name that fell back to workload identity. GitHub gives pull requests from forks neither secrets nor OIDC tokens, so the Action warns and skips cache activity for them.

A shared workflow can pass an access key to every caller. To use workload identity anyway, set `develocity-auth` to `workload-identity`, and the Action ignores the key and logs that it did. With `develocity-auth` unset or set to `access-key`, the Action behaves as described above. Any other value fails the step. This step always uses workload identity:

```yaml
- name: Develocity Artifact Cache
  uses: gradle/develocity-artifact-cache-github-action@v1
  with:
    develocity-edge-url: https://develocity.example.com
    develocity-auth: workload-identity
```

A GitHub OIDC token lasts about five minutes, so the Action exchanges it for a Develocity token right after requesting it. The main step requests one for the restore, and the post step requests another for the store. `develocity-token-expiry` sets how long each Develocity token lasts.

<a id="action-gradle"></a>

### Gradle

This workflow caches Gradle dependencies through the Action.

```yaml
name: Artifact Cache Gradle template

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Set up Java
        uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: 21

      - name: Develocity Artifact Cache
        uses: gradle/develocity-artifact-cache-github-action@v1
        with:
          develocity-url: https://develocity.example.com
          develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

      - name: Build project
        run: ./gradlew build
```

<a id="action-maven"></a>

### Maven

This workflow caches Maven dependencies through the Action.

```yaml
name: Artifact Cache Maven template

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Set up Java
        uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: 21

      - name: Develocity Artifact Cache
        uses: gradle/develocity-artifact-cache-github-action@v1
        with:
          develocity-url: https://develocity.example.com
          develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

      - name: Build project
        run: ./mvnw install
```

<a id="action-npm"></a>

### npm

This workflow caches npm dependencies through the Action.

```yaml
name: Artifact Cache npm template

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Set up Java
        uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: 21

      - name: Develocity Artifact Cache
        uses: gradle/develocity-artifact-cache-github-action@v1
        with:
          develocity-url: https://develocity.example.com
          develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

      - name: Build project
        run: npm ci && npm run build
```

<a id="action-job-summary"></a>

### Job Summary

After the run, the Action adds a table to the GitHub job summary with a row for the restore and a row for the store. A read-only cache has no store row.

![GitHub Actions job summary listing Restore and Store rows with outcome](https://docs.develocity.ai/artifact-cache/1.9/how-to/configure-github-actions/../../_images/github-action-job-summary.png)

GitHub Actions Job Summary for the Artifact Cache Action

Each row starts with the outcome of its phase:

*   Failure, when the CLI reported an error or whole images failed
    
*   Warning, when some entries failed, the CLI reported no metrics, or the phase did not run
    
*   Success, otherwise
    

For a phase that did not run, the Notes column gives the reason, such as a store skipped after a failed build. In a dry run (`--dry-run` in `additional-cli-args`), the CLI writes no metrics, so each row shows Success and a note saying so. Gradle and Maven builds also report cache metrics in their Build Scan.

<a id="action-failures"></a>

### Warnings and Failures

A cache problem never fails the build. When the restore or the store fails, the Action logs a warning and the build runs without the cache. The same goes for a problem with the runner or the network. Examples are a runner with no suitable JDK, an unreachable CLI download, and a credential Develocity rejects. The Action stores the cache even after a failed restore, so the cache can recover.

The Action fails the step only for a misconfigured workflow or a CLI that fails its checksum:

*   neither or both of `develocity-url` and `develocity-edge-url`, or an endpoint that is not a valid HTTP or HTTPS URL
    
*   an unknown `develocity-auth` value, or a value other than `true` or `false` for `cache-read-only`, `develocity-url-allow-insecure`, `cli-repository-allow-insecure`, or `allow-untrusted`
    
*   a malformed `cli-version`, `cli-repository`, `cli-repository-header`, or `cli-sha256`
    
*   a flag the Action controls in `additional-cli-args`
    
*   a downloaded CLI that does not match its expected checksum
    

<a id="action-inputs"></a>

### Action Inputs

   
| Input | Required | Default | Description |
| --- | --- | --- | --- |
| `develocity-url` | One of |  | Develocity server URL, passed to the CLI as `--dv-server`. |
| `develocity-edge-url` | One of |  | Develocity Edge URL, passed to the CLI as `--dv-edge` to target an Edge directly instead of a server. |
| `develocity-auth` | No |  | With `workload-identity`, the Action always authenticates with an OIDC token and ignores `develocity-access-key`. Unset or `access-key` uses `develocity-access-key` when it is set, and an OIDC token otherwise (see [Authenticating With an OIDC Token](#oidc-authentication)). Any other value fails the step. |
| `develocity-access-key` | No |  | Access key for the endpoint’s host (`develocity-url` or `develocity-edge-url`), in `<hostname>=<key_value>` form. The Action does not accept a bare key value, and reads no other key source: not the `DEVELOCITY_ACCESS_KEY` environment variable, and not local key files. It exchanges the key for a short-lived token that authenticates the restore, the store, and a CLI download from a Develocity Edge node, and does not export the token to other steps. When the key is unset or blank, the Action authenticates with an OIDC token instead (see [Authenticating With an OIDC Token](#oidc-authentication)). Ignored when `develocity-auth` is `workload-identity`. A missing key never fails the build. |
| `develocity-oidc-audience` | No | Origin of the chosen endpoint | Audience of the GitHub OIDC token the Action requests. It must match the audience on the workload identity rule. Ignored when the Action authenticates with `develocity-access-key`. |
| `develocity-token-expiry` | No | `2` | Lifetime in hours of each short-lived token the Action gets in exchange for the access key or OIDC token. The main and post steps each get their own token, so it only has to last for one cache operation. |
| `cli-version` | No | Pinned general-availability version | CLI version to download. |
| `image-names` | No | Auto-generated name (see [Automatic Image Name Generation](#automatic-image-name-generation)) | Ordered image names, one per line. The first is the primary; the rest are restore-only fallbacks. |
| `additional-cli-args` | No |  | Extra arguments appended to every restore and store call, one per line, such as `--gradle-home=/home/runner/.gradle`. A flag the Action controls fails the step. That includes `--allow-untrusted-server`, which `allow-untrusted` replaces. |
| `cache-read-only` | No | `false` | When `true`, skip the store step. |
| `cli-repository` | No | Public Develocity download URL, or a Develocity Edge node when the endpoint (`develocity-url` or `develocity-edge-url`) addresses one running version 2.3.0 or later | The directory that holds the CLI JARs (see [Air-Gapped and Custom CLI Hosting](#air-gapped)). Set it to an internal mirror for air-gapped runners. A value here always takes precedence over downloading from an Edge node. Must be an HTTPS URL unless `cli-repository-allow-insecure` is set. |
| `cli-repository-header` | No |  | A single `Name: Value` HTTP header the Action sends with the CLI download, for an authenticated mirror. Provide it through a secret so the log masks it. A download from an Edge node authenticates with the Action’s own token, so the Action leaves the header out and logs a warning. A malformed value fails the step. |
| `cli-repository-allow-insecure` | No | `false` | Allow the CLI download over plain HTTP, from a `cli-repository` mirror or from the Edge node at an HTTP endpoint. The Edge node case also needs `develocity-url-allow-insecure`. Without this input, the Action rejects an HTTP download URL. Enable it only on a trusted internal network without TLS, because over HTTP the download and its credential travel without encryption. That credential is the `cli-repository-header` for a mirror, or the short-lived token for an Edge node. |
| `develocity-url-allow-insecure` | No | `false` | Allow `develocity-url` or `develocity-edge-url` to be an HTTP URL, over which the access key or OIDC token travels without encryption. Without this input, the Action sends no credential to an HTTP endpoint, and warns and skips all cache activity. The Jenkins plugin’s equivalent is `develocityUrlAllowInsecure`. |
| `allow-untrusted` | No | `false` | Accept the endpoint’s TLS certificate without verifying it. This applies to the token exchange, the CLI download from the endpoint’s Edge node, and the CLI itself (`--allow-untrusted-server`). The Action still verifies a `cli-repository` mirror and any redirect away from the endpoint. Where you can, install the certificate on the runner instead. The Jenkins plugin’s equivalent is `allowUntrusted`. |
| `cli-sha256` | No |  | Expected SHA-256 of the CLI JAR. Set it when `cli-version` pins a version the Action ships no checksum for. Without it, the Action refuses that version and skips caching. |

<a id="air-gapped"></a>

### Air-Gapped and Custom CLI Hosting

The Action downloads the CLI once, in its main step, and the post step reuses it. It takes the CLI from the first of these sources that applies:

1.  `cli-repository`, when set.
    
2.  The Develocity Edge node, when the endpoint (`develocity-url` or `develocity-edge-url`) addresses an Edge node running version 2.3.0 or later, connected to Develocity 2026.3.0 or later. The Action recognizes the Edge node from the response to its short-lived token exchange and authenticates the download with that token. This route needs no extra configuration, and it is the same Edge-serving mechanism [Downloading From an Edge Node](https://docs.develocity.ai/artifact-cache/1.9/download-the-cli/#downloading-from-an-edge-node) describes for the manual setup. An Edge node that already holds the requested version serves it without the runner reaching the public host. An older Edge node is not used.
    
3.  The public Develocity download host.
    

Checksum verification works the same whichever source provides the CLI: it is checked against `cli-sha256`, or else the checksum the Action ships for that version. If a pinned `cli-version` has neither, the Action refuses it before downloading anything, then warns and skips all cache activity for the job.

The Action retries a download that fails with a network error or an HTTP 408, 429, or 5xx response. It makes up to four attempts, waiting 1, 2, and then 4 seconds between them. It does not retry any other response, or a failure to write the CLI to disk on the runner.

The Action does not fall back from one of these sources to another. If the chosen source is still unreachable after retries, the Action warns and skips all cache activity for the job, and the build runs without the cache.

To serve the CLI from your own network instead, set `cli-repository` to your mirror. `cli-repository` names the directory that holds the CLI JARs, and the Action requests `«cli-repository»/develocity-artifact-cache-cli-«version».jar` from it. The Jenkins plugin’s `cliRepository` uses the same layout, so one mirror serves both. A mirror laid out for versions before 1.1.1 still works for now (see [Upgrade Notes](#upgrade-notes)). For an authenticated mirror, add `cli-repository-header` with a `Name: Value` header supplied through a secret:

```yaml
- name: Develocity Artifact Cache
  uses: gradle/develocity-artifact-cache-github-action@v1
  with:
    develocity-url: https://develocity.example.com
    cli-repository: https://mirror.example.com/artifact-cache-cli
    cli-repository-header: ${{ secrets.MIRROR_AUTH_HEADER }}
```

<a id="automatic-image-name-generation"></a>

## Automatic Image Name Generation

By default, Artifact Cache CLI generates an image name automatically when you do not set one explicitly. Automatic generation is the recommended approach: it derives the name from the build context, your project’s repository data, and the CI runner’s environment, so you do not have to choose or maintain a name yourself.

The Artifact Cache CLI inspects the following environment variables when generating image names for GitHub Runners:

*   `GITHUB_WORKFLOW`
    
*   `GITHUB_REF`
    
*   `GITHUB_BASE_REF`
    
*   `GITHUB_JOB`
    
*   `GITHUB_ACTIONS`
    
*   `RUNNER_ARCH`
    
*   `RUNNER_OS`
    

On restore the Action also tries a fallback so a branch with no cache of its own can still restore from a related branch: a pull request falls back to its base branch’s cache, and a push or manually-dispatched run falls back to the repository’s default branch, so a new branch restores instead of starting cold.

> [!NOTE]
> The fallback needs Artifact Cache CLI 1.9.0 or later. Earlier versions generate a fallback name that matches no stored cache, so a pull request or a new branch starts with an empty cache.

The Action passes two branch variables to both the restore and the store. `DEVELOCITY_ARTIFACT_CACHE_BRANCH_NAME` holds the build’s branch. `DEVELOCITY_ARTIFACT_CACHE_BASE_BRANCH_NAME` holds the branch to fall back to, as `refs/heads/«branch»`. That is a pull request’s target branch, or the default branch on a push or a manual run. If the job’s environment already sets either variable, even to an empty value, the Action keeps that value.

<a id="overriding-the-generated-name"></a>

### Overriding the Generated Name

Set an explicit name only when you need to control the value, for example to share one cache across differently-named jobs. With the GitHub Action, provide it through the `image-names` input. With the manual CLI setup, provide it with the `--image-name` CLI option.

<a id="matrix-builds"></a>

### Matrix Builds

The auto-generated image name isolates caches per job and by the runner’s operating system and architecture. When a build matrix adds other axes, such as a Java or tool version, those jobs share one cache unless you isolate them.

With the GitHub Action, add a dedicated `image-names` entry to isolate a sensitive axis.

For example, in a job with a `strategy.matrix.java` axis, reference that axis in `image-names`. A custom `image-names` value replaces the auto-generated name entirely, so include the runner’s operating system and architecture (which the generated name isolates on) alongside the extra axis, and use a stable prefix to namespace the project:

```yaml
name: Develocity Artifact Cache
uses: gradle/develocity-artifact-cache-github-action@v1
with:
  develocity-url: https://develocity.example.com
  develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
  image-names: my-project-${{ runner.os }}-${{ runner.arch }}-java${{ matrix.java }}
```

Overriding the name also forgoes the automatic branch fallback described above, so prefer the generated name unless a matrix axis makes an override necessary.

<a id="manual-cli-setup"></a>

## Manual CLI Setup

Use the manual setup when you cannot use the GitHub Action: on air-gapped or restricted-network runners, when your organization does not permit third-party GitHub Actions, or when you need control beyond the Action’s inputs. This approach downloads the CLI and runs the restore and store commands yourself.

For an air-gapped runner that can still use the Action, point the Action at an internal mirror with `cli-repository` instead (see [Air-Gapped and Custom CLI Hosting](#air-gapped)). Choose the manual setup when you also want to manage the restore and store steps yourself.

<a id="storing-credentials"></a>

### Storing Credentials

In your GitHub repository settings, navigate to **Settings > Secrets and variables > Actions** and add:

 
| Secret Name | Value |
| --- | --- |
| `DEVELOCITY_ACCESS_KEY` | Your Develocity access key: `<hostname>=<key_value>` |
| `ARTIFACT_CACHE_REPO_USERNAME` | Your username for your internal artifact repository |
| `ARTIFACT_CACHE_REPO_PASSWORD` | Your password for your internal artifact repository |

> [!NOTE]
> If you use a custom internal artifact repository location, verify the "Provision and configure Artifact Cache" step. Ensure the `curl` command’s authentication parameters (`--user`) and download URL match your repository’s requirements.

<a id="gradle-setup"></a>

### Gradle Project Setup

<a id="configuration"></a>

#### Configuration

Add these environment variables to your workflow:

```yaml
env:
  # Artifact Cache CLI Configuration
  ARTIFACT_CACHE_CLI_VERSION: 1.9.0
  ARTIFACT_CACHE_IMAGE: my-gradle-project  (1)

  # Develocity Server
  DV_SERVER: https://develocity.example.com  (2)

  # Authentication (from secrets)
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
  ARTIFACT_CACHE_REPO_USERNAME: ${{ secrets.ARTIFACT_CACHE_REPO_USERNAME }}
  ARTIFACT_CACHE_REPO_PASSWORD: ${{ secrets.ARTIFACT_CACHE_REPO_PASSWORD }}

  # Repository location - replace with your internal artifact repository URL
  ARTIFACT_CACHE_REPO: https://your-internal-repo.example.com/path/to/artifact-cache-cli  (3)

  # CLI Configuration
  ARTIFACT_CACHE_CLI_FILENAME: develocity-artifact-cache-cli
  ARTIFACT_CACHE_OPTS: "--gradle-home=/home/runner/.gradle"  (4)
```

1. Optional - leave unset for automatic generation (Recommended)
2. Replace with your Develocity server URL
3. Replace with the URL to your internal artifact repository where you’ve hosted the CLI binary
4. Assumes a Linux (Ubuntu) runner. macOS runners use /Users/runner, and Windows runners need the approach in Windows GitHub Runners. Also adjust for a custom Gradle User Home location.

<a id="complete-workflow-example"></a>

#### Complete Workflow Example

```yaml
name: Artifact Cache CLI Gradle template

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      # Artifact Cache Configuration
      ARTIFACT_CACHE_CLI_VERSION: 1.9.0
      # Develocity
      DV_SERVER: https://<develocity.address>
      DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
      # Artifact Cache CLI
      ARTIFACT_CACHE_REPO_USERNAME: ${{ secrets.ARTIFACT_CACHE_REPO_USERNAME }}
      ARTIFACT_CACHE_REPO_PASSWORD: ${{ secrets.ARTIFACT_CACHE_REPO_PASSWORD }}
      ARTIFACT_CACHE_REPO: https://your-internal-repo.example.com/path/to/artifact-cache-cli
      # Misc
      ARTIFACT_CACHE_CLI_FILENAME: develocity-artifact-cache-cli
      ARTIFACT_CACHE_OPTS: "--gradle-home=/home/runner/.gradle"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Setup Java (Required for Artifact Cache)
        uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: 21

      - name: Provision and configure Artifact Cache
        shell: bash
        run: |
          TOOL_DIR="${{ runner.tool_cache }}/develocity/artifact-cache/${{ env.ARTIFACT_CACHE_CLI_VERSION }}"
          JAR_PATH="$TOOL_DIR/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}.jar"
          mkdir -p "$TOOL_DIR"
          
          # Download if not present
          if [ ! -f "$JAR_PATH" ]; then
            echo "Downloading Artifact Cache CLI v${{ env.ARTIFACT_CACHE_CLI_VERSION }}..."
            curl --location --fail --silent --show-error \
              --connect-timeout 5 --max-time 30 \
              --retry 3 --retry-delay 3 --retry-max-time 60 \
              --user "$ARTIFACT_CACHE_REPO_USERNAME:$ARTIFACT_CACHE_REPO_PASSWORD" \
              "${{ env.ARTIFACT_CACHE_REPO }}/com/gradle/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}/${{ env.ARTIFACT_CACHE_CLI_VERSION }}/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}-${{ env.ARTIFACT_CACHE_CLI_VERSION }}.jar" \
              --output "$JAR_PATH"
          fi
          
          CMD="${{ env[format('JAVA_HOME_21_{0}', runner.arch)] }}/bin/java -jar $JAR_PATH"
          echo "ARTIFACT_CACHE_CMD=$CMD" >> $GITHUB_ENV
          
          CMD_OPTS="--dv-server=$DV_SERVER $ARTIFACT_CACHE_OPTS"
          echo "ARTIFACT_CACHE_CMD_OPTS=$CMD_OPTS" >> $GITHUB_ENV

      - name: Restore from Artifact Cache
        id: restore_cache
        run: $ARTIFACT_CACHE_CMD restore $ARTIFACT_CACHE_CMD_OPTS

      - name: Warn on Cache Restore Failure
        if: steps.restore_cache.outcome == 'failure'
        run: 'echo "WARNING: Could not restore the Artifact Cache. The build will proceed but may be slower."'

      - name: Build project
        run: ./gradlew build

      - name: Store to Artifact Cache
        if: success()
        id: store_cache
        continue-on-error: true
        run: $ARTIFACT_CACHE_CMD store $ARTIFACT_CACHE_CMD_OPTS

      - name: Warn on Cache Store Failure
        if: steps.store_cache.outcome == 'failure'
        run: 'echo "WARNING: Failed to store in the Artifact Cache."'

      - name: Persist Artifact Cache Logs
        uses: actions/upload-artifact@v7
        with:
          name: artifact-cache.log
          path: '~/.develocity/artifact-cache/artifact-cache.log'
          retention-days: 7
```

<a id="key-steps-explained"></a>

#### Key Steps Explained

**Checkout Repository (First)** — Always checkout before restoring cache. This ensures the repository context is available for image name generation and allows the Setup Cache to restore shared build logic content into the project directory.

**Setup Java** — The Artifact Cache CLI requires Java 21. Use `actions/setup-java` with `java-version: 21`.

**Provision and Configure** — Downloads the Artifact Cache CLI JAR if not already cached in the runner’s tool cache. Sets up environment variables for subsequent steps.

**Restore from Artifact Cache** — Runs before the build to restore cached dependencies. Uses `continue-on-error` behavior via conditional warning.

**Store to Artifact Cache** — Runs after successful build to update the cache. Uses `if: success()` and `continue-on-error: true` to not fail the build if store fails.

**Persist Logs** — Uploads diagnostic logs for troubleshooting. Recommended for all workflows.

<a id="shared-build-logic-caching"></a>

##### Shared Build Logic Caching

Starting with Artifact Cache CLI version 1.1.0, the Setup Cache automatically caches shared Gradle build logic defined in `buildSrc` and included builds. This feature requires the [Develocity Gradle Plugin](https://docs.develocity.ai/gradle/4.6/gradle-plugin/) version 4.4.1 or later, and has the following constraints:

*   The absolute project checkout path must remain constant across builds. GitHub Actions uses a stable path by default (`/home/runner/work/<repository>/<repository>`).
    
*   The repository checkout must happen before the `restore` command.
    

<a id="maven-setup"></a>

### Maven Project Setup

<a id="configuration-2"></a>

#### Configuration

```yaml
env:
  # Change only these for Maven
  ARTIFACT_CACHE_IMAGE: my-maven-project (1)
  ARTIFACT_CACHE_OPTS: "--maven-home=/home/runner/.m2"  (2)
```

1. Optional - leave unset for automatic generation (Recommended)
2. Assumes a Linux (Ubuntu) runner. macOS runners use /Users/runner, and Windows runners need the approach in Windows GitHub Runners. For a custom Maven repository, add --maven-repository=/custom/path.

<a id="complete-workflow-example-2"></a>

#### Complete Workflow Example

```yaml
name: Artifact Cache CLI Maven template

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      # Artifact Cache Configuration
      ARTIFACT_CACHE_CLI_VERSION: 1.9.0
      # Develocity
      DV_SERVER: https://<develocity.address>
      DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
      # Artifact Cache CLI
      ARTIFACT_CACHE_REPO_USERNAME: ${{ secrets.ARTIFACT_CACHE_REPO_USERNAME }}
      ARTIFACT_CACHE_REPO_PASSWORD: ${{ secrets.ARTIFACT_CACHE_REPO_PASSWORD }}
      ARTIFACT_CACHE_REPO: https://your-internal-repo.example.com/path/to/artifact-cache-cli
      # Misc
      ARTIFACT_CACHE_CLI_FILENAME: develocity-artifact-cache-cli
      ARTIFACT_CACHE_OPTS: "--maven-home=/home/runner/.m2"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Setup Java (Required for Artifact Cache)
        uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: 21

      - name: Provision and configure Artifact Cache
        shell: bash
        run: |
          TOOL_DIR="${{ runner.tool_cache }}/develocity/artifact-cache/${{ env.ARTIFACT_CACHE_CLI_VERSION }}"
          JAR_PATH="$TOOL_DIR/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}.jar"
          mkdir -p "$TOOL_DIR"
          
          # Download if not present
          if [ ! -f "$JAR_PATH" ]; then
            echo "Downloading Artifact Cache CLI v${{ env.ARTIFACT_CACHE_CLI_VERSION }}..."
            curl --location --fail --silent --show-error \
              --connect-timeout 5 --max-time 30 \
              --retry 3 --retry-delay 3 --retry-max-time 60 \
              --user "$ARTIFACT_CACHE_REPO_USERNAME:$ARTIFACT_CACHE_REPO_PASSWORD" \
              "${{ env.ARTIFACT_CACHE_REPO }}/com/gradle/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}/${{ env.ARTIFACT_CACHE_CLI_VERSION }}/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}-${{ env.ARTIFACT_CACHE_CLI_VERSION }}.jar" \
              --output "$JAR_PATH"
          fi
          
          CMD="${{ env[format('JAVA_HOME_21_{0}', runner.arch)] }}/bin/java -jar $JAR_PATH"
          echo "ARTIFACT_CACHE_CMD=$CMD" >> $GITHUB_ENV
          
          CMD_OPTS="--dv-server=$DV_SERVER $ARTIFACT_CACHE_OPTS"
          echo "ARTIFACT_CACHE_CMD_OPTS=$CMD_OPTS" >> $GITHUB_ENV

      - name: Restore from Artifact Cache
        id: restore_cache
        run: $ARTIFACT_CACHE_CMD restore $ARTIFACT_CACHE_CMD_OPTS

      - name: Warn on Cache Restore Failure
        if: steps.restore_cache.outcome == 'failure'
        run: 'echo "WARNING: Could not restore the Artifact Cache. The build will proceed but may be slower."'

      - name: Build project
        run: ./mvnw install

      - name: Store to Artifact Cache
        if: success()
        id: store_cache
        continue-on-error: true
        run: $ARTIFACT_CACHE_CMD store $ARTIFACT_CACHE_CMD_OPTS

      - name: Warn on Cache Store Failure
        if: steps.store_cache.outcome == 'failure'
        run: 'echo "WARNING: Failed to store in the Artifact Cache."'

      - name: Persist Artifact Cache Logs
        uses: actions/upload-artifact@v7
        with:
          name: artifact-cache.log
          path: '~/.develocity/artifact-cache/artifact-cache.log'
          retention-days: 7
```

<a id="custom-maven-repository-path"></a>

#### Custom Maven Repository Path

If your project uses a custom Maven repository location (via `-Dmaven.repo.local` or `settings.xml`), add the `--maven-repository` option:

```yaml
env:
  ARTIFACT_CACHE_OPTS: "--maven-home=/home/runner/.m2 --maven-repository=/custom/repo"
```

<a id="npm-setup"></a>

### npm Project Setup

<a id="configuration-3"></a>

#### <a id="configuration-4"></a>Configuration

```yaml
env:
  # Change only these for npm
  ARTIFACT_CACHE_IMAGE: my-npm-project (1)
  ARTIFACT_CACHE_OPTS: "--npm-home=/home/runner/.npm"  (2)
```

1. Optional - leave unset for automatic generation (Recommended)
2. Assumes a Linux (Ubuntu) runner. macOS runners use /Users/runner, and Windows runners need the approach in Windows GitHub Runners.

<a id="complete-workflow-example-3"></a>

#### Complete Workflow Example

```yaml
name: Artifact Cache CLI npm template

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      # Artifact Cache Configuration
      ARTIFACT_CACHE_CLI_VERSION: 1.9.0
      # Develocity
      DV_SERVER: https://<develocity.address>
      DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
      # Artifact Cache CLI
      ARTIFACT_CACHE_REPO_USERNAME: ${{ secrets.ARTIFACT_CACHE_REPO_USERNAME }}
      ARTIFACT_CACHE_REPO_PASSWORD: ${{ secrets.ARTIFACT_CACHE_REPO_PASSWORD }}
      ARTIFACT_CACHE_REPO: https://your-internal-repo.example.com/path/to/artifact-cache-cli
      # Misc
      ARTIFACT_CACHE_CLI_FILENAME: develocity-artifact-cache-cli
      ARTIFACT_CACHE_OPTS: "--npm-home=/home/runner/.npm"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Setup Java (Required for Artifact Cache)
        uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: 21

      - name: Provision and configure Artifact Cache
        shell: bash
        run: |
          TOOL_DIR="${{ runner.tool_cache }}/develocity/artifact-cache/${{ env.ARTIFACT_CACHE_CLI_VERSION }}"
          JAR_PATH="$TOOL_DIR/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}.jar"
          mkdir -p "$TOOL_DIR"
          
          # Download if not present
          if [ ! -f "$JAR_PATH" ]; then
            echo "Downloading Artifact Cache CLI v${{ env.ARTIFACT_CACHE_CLI_VERSION }}..."
            curl --location --fail --silent --show-error \
              --connect-timeout 5 --max-time 30 \
              --retry 3 --retry-delay 3 --retry-max-time 60 \
              --user "$ARTIFACT_CACHE_REPO_USERNAME:$ARTIFACT_CACHE_REPO_PASSWORD" \
              "${{ env.ARTIFACT_CACHE_REPO }}/com/gradle/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}/${{ env.ARTIFACT_CACHE_CLI_VERSION }}/${{ env.ARTIFACT_CACHE_CLI_FILENAME }}-${{ env.ARTIFACT_CACHE_CLI_VERSION }}.jar" \
              --output "$JAR_PATH"
          fi
          
          CMD="${{ env[format('JAVA_HOME_21_{0}', runner.arch)] }}/bin/java -jar $JAR_PATH"
          echo "ARTIFACT_CACHE_CMD=$CMD" >> $GITHUB_ENV
          
          CMD_OPTS="--dv-server=$DV_SERVER $ARTIFACT_CACHE_OPTS"
          echo "ARTIFACT_CACHE_CMD_OPTS=$CMD_OPTS" >> $GITHUB_ENV

      - name: Restore from Artifact Cache
        id: restore_cache
        run: $ARTIFACT_CACHE_CMD restore $ARTIFACT_CACHE_CMD_OPTS

      - name: Warn on Cache Restore Failure
        if: steps.restore_cache.outcome == 'failure'
        run: 'echo "WARNING: Could not restore the Artifact Cache. The build will proceed but may be slower."'

      - name: Build project
        run: npm ci && npm run build

      - name: Store to Artifact Cache
        if: success()
        id: store_cache
        continue-on-error: true
        run: $ARTIFACT_CACHE_CMD store $ARTIFACT_CACHE_CMD_OPTS

      - name: Warn on Cache Store Failure
        if: steps.store_cache.outcome == 'failure'
        run: 'echo "WARNING: Failed to store in the Artifact Cache."'

      - name: Persist Artifact Cache Logs
        uses: actions/upload-artifact@v7
        with:
          name: artifact-cache.log
          path: '~/.develocity/artifact-cache/artifact-cache.log'
          retention-days: 7
```

<a id="sonar-setup"></a>

## SonarScanner Caching

SonarScanner caching layers on top of your existing project setup. The Artifact Cache CLI auto-detects the SonarScanner user home from `$SONAR_USER_HOME`, falling back to `~/.sonar`. No extra configuration is required as long as the directory exists when `restore` or `store` runs. For Python, the Artifact Cache CLI supports both the general-purpose `sonar-scanner-cli` and the `pysonar` package SonarSource recommends.

If your workflow caches `~/.sonar` or `~/.sonar/cache` via `actions/cache`, remove that step. The Artifact Cache CLI handles SonarScanner caching automatically.

> [!TIP]
> Expect image growth of roughly 50 to 100 MB per analyzer version stored.

If the directory is created during build execution, pin it explicitly by appending `--sonar-home` to the CLI arguments. With the GitHub Action, add it to `additional-cli-args`; with the manual setup, add it to `ARTIFACT_CACHE_OPTS`:

```yaml
env:
  ARTIFACT_CACHE_OPTS: "--gradle-home=/home/runner/.gradle --sonar-home=/home/runner/.sonar"
```

To opt out of SonarScanner caching even when the user home is present, exclude it from auto-detection:

```yaml
env:
  ARTIFACT_CACHE_OPTS: "--gradle-home=/home/runner/.gradle --no-autodetect=SONAR"
```

<a id="docker-setup"></a>

## Docker Image Caching

The Artifact Cache CLI caches the runner’s Docker images whenever a `docker` executable is on `PATH`, which is the case on GitHub-hosted Linux runners. No extra configuration is required. See [Docker Image Caching](https://docs.develocity.ai/artifact-cache/1.9/concepts/docker-images/) for which images are cached.

To also cache the base images a `docker/setup-buildx-action` builder pulls, add `--enable-docker-builders` to `additional-cli-args` and put the Artifact Cache step **after** `setup-buildx-action`:

```yaml
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v4
- uses: gradle/develocity-artifact-cache-github-action@v1
  with:
    develocity-url: https://develocity.example.com
    develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
    additional-cli-args: --enable-docker-builders
- run: docker buildx build .
```

`setup-buildx-action` creates a builder in its step and removes it in its post step, and GitHub Actions runs post steps in reverse order. In this order, the restore finds the builder running and the store runs before the builder is removed. In the opposite order, the base images the builder pulls aren’t cached, and the image store is cached as usual.

To leave images out of the cache, add `--docker-exclude` to `additional-cli-args`, or to `ARTIFACT_CACHE_OPTS` with the manual setup. Only `store` uses it, and `restore` accepts it and ignores it:

```yaml
- uses: gradle/develocity-artifact-cache-github-action@v1
  with:
    develocity-url: https://develocity.example.com
    develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
    additional-cli-args: --docker-exclude=ghcr.io/example/*
```

To opt out of Docker caching, exclude it from auto-detection with `--no-autodetect=DOCKER`.

<a id="windows-runners"></a>

## Windows GitHub Runners

Windows runners require additional configuration due to path handling differences.

```yaml
- name: Set Dynamic Environment Variables
  shell: bash
  run: |
    NORMALIZED_TOOL_PATH=$(echo "${{ runner.tool_cache }}" | sed 's|\\|/|g' | sed 's|^C:/|/c/|i')
    echo "DV_ARTIFACT_CACHE_JAR_PATH=$NORMALIZED_TOOL_PATH/develocity/${{ env.DV_ARTIFACT_CACHE_CLI_VERSION }}/develocity-artifact-cache-cli.jar" >> $GITHUB_ENV
    echo "DV_CACHE_DIR=$HOME/.gradle" >> $GITHUB_ENV
    NORMALIZED_JAVA_PATH=$(echo "${{ env[format('JAVA_HOME_21_{0}', runner.arch)] }}" | sed 's|\\|/|g' | sed 's|^C:/|/c/|i')
    echo "DV_JAVA=$NORMALIZED_JAVA_PATH/bin/java" >> $GITHUB_ENV
#...
- name: Restore Artifact Cache
  id: restore_cache
  shell: bash
  run: |
    $DV_JAVA -jar "${{ env.DV_ARTIFACT_CACHE_JAR_PATH }}" \
    restore \
    --server="${{ env.DV_SERVER }}" \
    --image-name="${{ env.DV_ARTIFACT_CACHE_IMAGE }}" \
    --gradle-home="${{ env.DV_CACHE_DIR }}"
```

<a id="related-documentation"></a>

## Related Documentation

*   [CLI Commands Reference](https://docs.develocity.ai/artifact-cache/1.9/reference/cli-commands/)
    
*   [Workload Identity](https://docs.develocity.ai/2026.3/administration/access-control/workload-identity/) (configuring Develocity to accept OIDC tokens)
    
*   [Develocity Artifact Cache GitHub Action README](https://github.com/gradle/develocity-artifact-cache-github-action#readme) (supported runners, versioning, and access-key resolution in detail)
    

<a id="upgrade-notes"></a>

## Appendix A: Upgrade Notes

<a id="upgrading-from-the-github-action-1-0-0-or-earlier"></a>

### Upgrading From the GitHub Action 1.0.0 or Earlier

Change these before you upgrade, where they apply:

*   If `develocity-url` or `develocity-edge-url` is an `http` URL, set `develocity-url-allow-insecure: true`. Otherwise the Action skips the cache.
    
*   Replace `--allow-untrusted-server` in `additional-cli-args` with `allow-untrusted: true`. The flag now fails the step.
    
*   If `cli-version` pins a version the Action ships no checksum for, set `cli-sha256`. Otherwise the Action refuses that version and skips the cache.
    
*   Point `cli-repository` at the directory that holds the CLI JARs. Version 1.0.0 looked one directory further down, in `«cli-repository»/develocity-artifact-cache-cli/`. The old layout still works with a warning, but a future release drops it.
    
*   Fix any mistake listed under [Warnings and Failures](#action-failures). Those now fail the step instead of logging a warning.
    

These changes need no action:

*   A blank `develocity-access-key` falls back to workload identity instead of skipping the cache (see [Authenticating With an OIDC Token](#oidc-authentication)). A pull request from a fork still skips, because GitHub gives it no OIDC token.
    
*   New inputs: `develocity-edge-url`, `develocity-auth`, `develocity-oidc-audience`, `develocity-url-allow-insecure`, and `allow-untrusted`.
    
*   The post step gets its own token, so `develocity-token-expiry` only has to last for one cache operation.
    
*   The store also runs after a failed restore.
    
*   The job summary shows an outcome for each phase, and a Notes column replaces the Errors column (see [Job Summary](#action-job-summary)).
    
*   The Action retries a CLI download only after a network error or an HTTP 408, 429, or 5xx response. It makes up to four attempts, 1, 2, and then 4 seconds apart.
    
*   The Action logs a warning when it leaves out `cli-repository-header` for a download from an Edge node.
    
*   Both phases receive the branch variables, and a value set in the job’s environment takes precedence (see [Automatic Image Name Generation](#automatic-image-name-generation)).