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

<a id="component-eol-banner"></a>

You are viewing **Develocity Artifact Cache CLI 1.0**. To view the latest available version of the docs, see [1.1](https://docs.gradle.com/develocity/artifact-cache/1.1/concepts/images/).

# Understanding Images

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

An **image** is a metadata file that lists all artifacts needed for a specific CI job. Understanding images is important to maintaining Develocity Artifact Cache and Setup Cache efficiency.

<a id="what-is-an-image"></a>

## What Is an Image?

An image is a metadata file stored at the Edge that includes:

*   List of build artifacts, like dependencies and/or build initialization inputs
    
*   OS and architecture information (Linux x64, Windows ARM64, etc.)
    
*   Build tool content type metadata (Maven, Gradle, npm)
    
*   Generation number (how many times the job has run)
    

> [!TIP]
> An image **isn’t** a container image or archive file. It’s a lightweight metadata file that references artifacts stored separately at the Edge.

> [!NOTE]
> Artifacts with a `-SNAPSHOT` version qualifier are not stored in images, as snapshot versions are mutable and not safe to cache.

<a id="gradle-changing-modules"></a>

> [!WARNING]
> Gradle supports marking any dependency as a changing module via setChanging(true), regardless of its version name. Develocity Artifact Cache and Setup Cache identifies mutable artifacts by name only — it excludes versions ending in -SNAPSHOT, but cannot detect release-named artifacts configured as changing. If a build marks a release-versioned dependency as changing, Develocity Artifact Cache and Setup Cache will cache those artifacts as if they were stable. Gradle may fetch a newer version of the artifact, but during the next store operation the file will appear to already be present and won’t be uploaded — causing stale artifacts to persist in the image.

<a id="how-images-work"></a>

## How Images Work

<a id="image-lifecycle"></a>

### Image Lifecycle

1.  **First Build:** No image exists, Artifact Cache CLI creates a new one
    
2.  **Store:** Artifacts are uploaded, image is created
    
3.  **Subsequent Builds:** Image is found, artifacts are restored
    
4.  **Growth:** New dependencies added over time expand the image
    
5.  **Refresh:** Periodic rebuilds create clean images
    

<a id="image-naming"></a>

### Image Naming

Every image should have a unique name that identifies the CI job, branch, and environment.

<a id="properties-of-a-good-image-name"></a>

#### Properties of a Good Image Name

A well-chosen image name should be unique and stable to ensure effective caching.

**Unique:**

*   Must uniquely identify the specific build context
    
*   Include the project name, branch, and environment characteristics (OS, architecture)
    
*   Prevents cache collisions between different builds
    

**Stable:**

*   Avoid including dynamic values like timestamps, run numbers, or commit IDs
    
*   Should remain constant across builds in the same context
    

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

#### Automatic Generation

If you don’t provide an `--image-name`, the Artifact Cache CLI automatically generates one based on specifics of each supported CI Environment (GitHub Actions, Jenkins).

<a id="manual-naming]"></a>

#### Manual Naming

You can specify custom image names using the `--image-name` option:

```bash
java -jar develocity-artifact-cache-cli.jar restore \
  --dv-server=https://develocity.example.com \
  --gradle-home=$HOME/.gradle \
  --image-name=my-project-main-build
```

**When to Use Manual Names:**

*   Sharing cache across multiple jobs
    
*   Debugging specific cache issues
    
*   Non-standard CI platforms
    
*   Custom caching strategies
    

**Best Practices:**

*   Use descriptive names: `project-branch-platform`
    
*   Keep names stable (no timestamps or random values)
    
*   Include relevant context (branch, OS, arch if important)
    
*   Avoid special characters
    
*   Keep names short and use only alphanumeric characters and underscores
    

> [!NOTE]
> Image names that contain special characters or exceed the maximum length are automatically transformed into a compact representation. This is normal and doesn’t affect functionality, but the name shown in logs may differ from the name you provided.

<a id="multiple-fallback-images"></a>

#### Multiple Fallback Images

You can specify multiple image names for fallback scenarios:

```bash
--image-name=feature-branch-123 \
--image-name=main-branch
```

The Artifact Cache CLI tries each image in order. This is useful for feature branches that want to fall back to the main branch cache.

<a id="image-growth-and-management"></a>

## Image Growth and Management

<a id="how-images-grow"></a>

### How Images Grow

Every time the `store` command runs, the image may grow.

**New Dependencies Added:**

If your project adds a new dependency, it’s added to the image.

**Transitive Dependencies:**

Dependency version bumps bring in new transitive dependencies.

**Build Tool Updates:**

Upgrading Gradle or Maven may download new distributions or plugins.

**What Happens:**

1.  New artifacts are uploaded to Edge node
    
2.  Image is updated to include new artifact references
    
3.  Image size grows incrementally
    

<a id="obsolete-artifacts"></a>

### Obsolete Artifacts

When you remove a dependency from your project, the artifact remains in the image.

**Impact:**

*   Image contains unnecessary artifacts
    
*   Restore downloads artifacts no longer needed
    
*   Edge node storage is wasted
    
*   Restore time increases slightly
    

**Solution:**

Smart cleanup (see below) automatically prevents obsolete artifacts from being stored for Gradle and Maven.

For npm, it’s advised to review defaults for and configure [image refresh](#image-refresh) to remove obsolete artifacts preventing image growth.

<a id="smart-cleanup"></a>

### Smart Cleanup

**Smart cleanup** automatically mitigates image growth by excluding unused artifacts during the store phase. The Artifact Cache CLI integrates with the build tool to identify which artifacts were actually used during the build. Only used artifacts are included in the stored image; unused artifacts already part of the image are filtered out.

This keeps the image lean after every store, without requiring a full image refresh (as covered in the [Image Refresh](#image-refresh) section).

Smart cleanup is supported for Gradle and Maven builds. Maven requires [Develocity Maven Extension](https://docs.gradle.com/develocity/maven/2.4/maven-extension/) version 2.4.0 or later.

Some types of Gradle cache are only removed when you upgrade Gradle or when that type of cacheable content grows too large.

<a id="image-refresh"></a>

## Image Refresh

<a id="what-is-refresh"></a>

### What Is a Refresh?

A **refresh** rebuilds the image from scratch based on the current build’s actual artifact usage.

**During Refresh:**

1.  Artifact Cache CLI tracks which artifacts are actually used during the build
    
2.  Old image is discarded
    
3.  New image is created with only used artifacts
    
4.  Unused artifacts remain on Edge but aren’t referenced
    

**After Refresh:**

*   Image contains only currently needed artifacts
    
*   Restore is faster (fewer artifacts)
    
*   Edge storage can reclaim space via normal eviction
    

<a id="when-to-refresh"></a>

### When to Refresh

Refresh is controlled by the `restore` command. The following options are only available on `restore`, not on `store`.

**Automatically:**

Use `--refresh-every-period` and/or `--refresh-every-generation`:

```bash
--refresh-every-period=P1M  # Refresh every month (default)
```

```bash
--refresh-every-generation=50  # Refresh every 50 builds
```

Combine both - whichever condition is met first triggers the refresh:

```bash
--refresh-every-period=P1M \
--refresh-every-generation=50
```

Force an immediate refresh at a scheduled interval (for example, a weekly triggered CI job):

```bash
--refresh-now
```

<a id="generation-counter"></a>

## Generation Counter

Each time a CI job runs (with a successful store), the image’s **generation number** increments.

<a id="image-storage-and-lifecycle"></a>

## Image Storage and Lifecycle

<a id="where-images-are-stored"></a>

### Where Images Are Stored

Images are stored at the Edge, not on the CI agent.

**During Restore:**

*   Artifact Cache CLI downloads image from the Edge
    
*   Uses image to determine which artifacts to download
    

**During Store:**

*   Artifact Cache CLI uploads new artifacts to Edge
    
*   Updates image on Edge
    

**CI Agent:**

*   No persistent image storage
    
*   Only temporary working files in `.develocity/`
    

<a id="image-eviction"></a>

### Image Eviction

Edge nodes recognize images as high-priority assets and apply a smart eviction policy that prevents frequent image regeneration. After a certain point, infrequently used images become subject to the Edge node’s cache storage eviction policy (least-recently used).

**Impact of Eviction:**

*   Next job creates a new image (cold cache)
    
*   One-time slowdown, then back to normal
    

**Prevention:**

*   Smart eviction policy is sufficient for most use cases
    
*   Adequately size Edge node storage
    
*   Run jobs frequently enough to keep images "hot"
    

<a id="image-isolation"></a>

## Image Isolation

<a id="by-environment"></a>

### By Environment

Images should be separate for different:

*   **Operating Systems:** Linux vs. Windows vs. macOS
    
*   **Architectures:** x64 vs. ARM64
    
*   **Branches:** `main` vs. `feature/xyz`
    
*   **Jobs:** Different workflow jobs have different images
    

This should be taken into consideration when naming images manually. Automatic image name generation already takes care of this on the supported CI Environments.

<a id="sharing-images"></a>

### Sharing Images

**Same Image Reused When:**

*   Same CI job runs multiple times
    
*   Branch and platform match
    
*   Image name matches (if manual naming)
    

**Different Images When:**

*   Different branches
    
*   Different OS/architecture
    
*   Different workflow/job names
    

<a id="best-practices"></a>

## Best Practices

<a id="let-automatic-generation-handle-names"></a>

### Let Automatic Generation Handle Names

For standard GitHub Actions and Jenkins workflows, automatic generation is recommended.

✓ **Do:**

```bash
java -jar artifact-cache-cli.jar restore \
  --dv-server=$DV_SERVER \
  --gradle-home=$HOME/.gradle
```

✗ **Avoid (unless needed):**

```bash
--image-name=build-${{ github.run_number }}  # Bad: changes per build
```

<a id="use-time-based-refresh"></a>

### Use Time-Based Refresh

Set a reasonable refresh period:

```bash
--refresh-every-period=P1M  # Monthly is a good default
```

<a id="consider-feature-branch-strategy"></a>

### Consider Feature Branch Strategy

Use fallback images for feature branches:

```bash
--image-name=feature-xyz \
--image-name=main
```

This allows feature branches to leverage the main branch cache initially.