Understanding Images


Limited Availability

The Develocity Artifact and Setup Cache is in Limited Availability.

Please contact the Develocity support team to get started with Develocity Artifact and Setup Cache.


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

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)

An image is NOT a container image or archive file. It’s a lightweight metadata file that references artifacts stored separately at the Edge.

How Images Work

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

Image Naming

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

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).

Manual Naming

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

java -jar {cli-name} 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

Multiple Fallback Images

You can specify multiple image names for fallback scenarios:

--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.

Image Growth and Management

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

Obsolete Artifacts

Problem:

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:

Image refresh (see below).

Image Refresh

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

When to Refresh

Automatically:

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

--refresh-every-period=P1M  # Refresh every month (default)
--refresh-every-generation=50  # Refresh every 50 builds

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

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

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

--refresh-now

Generation Counter

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

Image Storage and Lifecycle

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/

Image Eviction

Images follow the Edge node’s cache storage eviction policy (least-recently used). Infrequently used images may be evicted.

Impact of Eviction:

  • Next job creates a new image (cold cache)

  • One-time slowdown, then back to normal

Prevention:

  • Adequately size Edge node storage

  • Run jobs frequently enough to keep images "hot"

Image Isolation

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.

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

Best Practices

Let Automatic Generation Handle Names

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

Do:

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

Avoid (unless needed):

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

Use Time-Based Refresh

Set a reasonable refresh period:

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

Consider Feature Branch Strategy

Use fallback images for feature branches:

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

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