# Migrating a Java project to Chainguard Libraries

URL: https://edu.chainguard.dev/chainguard/libraries/java/migration.md
Last Modified: July 2, 2026
Tags: Chainguard Libraries, Java

How to migrate an existing Java project to pull dependencies from Chainguard Libraries

Chainguard Libraries for Java provides a curated repository of packages rebuilt from upstream sources and scanned for malware. Because Chainguard Libraries uses the standard Maven repository format, switching an existing project requires only a repository configuration change — no changes to your application code or dependency versions.
This guide walks through migrating an existing Java project to Chainguard Libraries, covering the two most common setups:
Direct access — your build tool connects directly to libraries.cgr.dev. This option is faster for initial evaluation and smaller-scale setups. Repository manager — your build tool connects to a repository manager (Cloudsmith, Google Artifact Registry, JFrog Artifactory, or Sonatype Nexus), which proxies requests to Chainguard Libraries. Prerequisites Before you begin, you need:
An existing Java project chainctl installed and authenticated Create an entitlement You must have an entitlement to Chainguard Libraries for Java with upstream fallback enabled.
To create an entitlement to Chainguard Libraries for Java and enable upstream fallback, which includes a default 7-day cooldown, run the following command:
chainctl libraries entitlements create --ecosystems=JAVA --policy=CHAINGUARD_AND_UPSTREAMAlternatively, you can create an entitlement in the Chainguard Console: while viewing the Java ecosystem page, follow the prompts to create an access token.
You can also configure cooldown policies after you create the entitlement. For example, to create and enforce a policy that disables the cooldown:
chainctl libraries policy create --name=java-disable-cooldown --cooldown-days=0 chainctl libraries policy enable --policy=java-disable-cooldown --ecosystem=JAVA --mode=ENFORCEIt can take up to 30 minutes for the fallback and cooldown policies to take effect. Learn more about cooldown and other policies in the Chainguard Libraries Access documentation.
Note: If you choose to manage your own fallback to upstream repositories, see the following docs pages for more information: Build configuration for direct access instructions or Global configuration for repo manager instructions. Note that configuring a public fallback bypasses the protections provided by Chainguard.
Step 1: Confirm your baseline build Before making any changes, confirm your project builds cleanly against Maven Central.
cd your-project ./mvnw install cd your-project ./gradlew build cd your-project bazel build //... All dependencies should download from Central and tests should pass. This gives you a baseline to compare against after migration.
Step 2: Generate a pull token You must be an Owner or have the libraries.java.pull_token_creator permission to create a pull token. You can create a pull token in the Chainguard Console, or via chainctl.
The following command creates the token and populates environment variables directly. Make sure to replace example.org with your own Chainguard org name:
eval $(chainctl auth pull-token --parent=example.org --repository=java --name=my-java-token --output=env)This results in values for the CHAINGUARD_JAVA_IDENTITY_ID and CHAINGUARD_JAVA_TOKEN variables. The token is named my-java-token, with a default expiration of 30 days. To configure the expiration, use the --ttl flag.
Learn more about command options in the chainctl documentation.
When configuring direct access, note that environment variables do not persist between terminal sessions. You must re-export them each time you open a new terminal, or add them to your shell profile. Learn more about pull tokens in the Access documentation.
Do not commit credentials to version control The Gradle build.gradle file and the Maven pom.xml are typically committed to a repository — always use environment variables for credentials rather than hardcoding token values directly in the file. Maven credentials live in ~/.m2/settings.xml, which is outside the project directory and not committed by default, but take care not to add it to your repository. Store tokens as CI secrets referenced via environment variables instead. If you accidentally commit credentials, delete the exposed token.
For more secure credential management, consider using a secrets manager such as 1Password CLI or Bitwarden, which can dynamically inject environment variables at runtime without storing token values in shell profiles or env files.
Verify your setup Run the following command for a connectivity and authentication check:
curl -u &#34;$CHAINGUARD_JAVA_IDENTITY_ID:$CHAINGUARD_JAVA_TOKEN&#34; \ https://libraries.cgr.dev/java/The command returns an HTML page listing repository directories. If credentials are invalid or expired, the command returns a 403 error instead of the directory listing.
Step 3: Configure repository access The https://libraries.cgr.dev/java/ endpoint is also the Chainguard Repository endpoint for Java. By default, it serves only Chainguard-built artifacts. When upstream fallback is enabled for your organization, the same endpoint can also serve requested versions from Maven Central under Chainguard security controls.
Direct access Maven repository and credentials configuration lives in a settings.xml file. For a project-local setup, place this file in the .mvn folder of your project and pass -s .mvn/settings.xml when running Maven commands.
First, create a .mvn directory if it doesn&rsquo;t already exist:
mkdir -p .mvnNext, create a .mvn/settings.xml file with the following content. This configuration sets the Chainguard remediated repository as the first source, followed by the standard Chainguard repository, with Maven Central set as invalid to avoid accidental unintended fallback to the public repository:
&lt;settings&gt; &lt;servers&gt; &lt;server&gt; &lt;id&gt;chainguard-remediated&lt;/id&gt; &lt;username&gt;${env.CHAINGUARD_JAVA_IDENTITY_ID}&lt;/username&gt; &lt;password&gt;${env.CHAINGUARD_JAVA_TOKEN}&lt;/password&gt; &lt;/server&gt; &lt;server&gt; &lt;id&gt;chainguard&lt;/id&gt; &lt;username&gt;${env.CHAINGUARD_JAVA_IDENTITY_ID}&lt;/username&gt; &lt;password&gt;${env.CHAINGUARD_JAVA_TOKEN}&lt;/password&gt; &lt;/server&gt; &lt;/servers&gt; &lt;profiles&gt; &lt;profile&gt; &lt;id&gt;chainguard&lt;/id&gt; &lt;repositories&gt; &lt;repository&gt; &lt;id&gt;chainguard-remediated&lt;/id&gt; &lt;url&gt;https://libraries.cgr.dev/java-remediated/&lt;/url&gt; &lt;releases&gt;&lt;enabled&gt;true&lt;/enabled&gt;&lt;/releases&gt; &lt;snapshots&gt;&lt;enabled&gt;false&lt;/enabled&gt;&lt;/snapshots&gt; &lt;/repository&gt; &lt;repository&gt; &lt;id&gt;chainguard&lt;/id&gt; &lt;url&gt;https://libraries.cgr.dev/java/&lt;/url&gt; &lt;releases&gt;&lt;enabled&gt;true&lt;/enabled&gt;&lt;/releases&gt; &lt;snapshots&gt;&lt;enabled&gt;false&lt;/enabled&gt;&lt;/snapshots&gt; &lt;/repository&gt; &lt;repository&gt; &lt;id&gt;central&lt;/id&gt; &lt;url&gt;https://invalid&lt;/url&gt; &lt;releases&gt;&lt;enabled&gt;true&lt;/enabled&gt;&lt;/releases&gt; &lt;snapshots&gt;&lt;enabled&gt;false&lt;/enabled&gt;&lt;/snapshots&gt; &lt;/repository&gt; &lt;/repositories&gt; &lt;pluginRepositories&gt; &lt;pluginRepository&gt; &lt;id&gt;chainguard-remediated&lt;/id&gt; &lt;url&gt;https://libraries.cgr.dev/java-remediated/&lt;/url&gt; &lt;releases&gt;&lt;enabled&gt;true&lt;/enabled&gt;&lt;/releases&gt; &lt;snapshots&gt;&lt;enabled&gt;false&lt;/enabled&gt;&lt;/snapshots&gt; &lt;/pluginRepository&gt; &lt;pluginRepository&gt; &lt;id&gt;chainguard&lt;/id&gt; &lt;url&gt;https://libraries.cgr.dev/java/&lt;/url&gt; &lt;releases&gt;&lt;enabled&gt;true&lt;/enabled&gt;&lt;/releases&gt; &lt;snapshots&gt;&lt;enabled&gt;false&lt;/enabled&gt;&lt;/snapshots&gt; &lt;/pluginRepository&gt; &lt;pluginRepository&gt; &lt;id&gt;central&lt;/id&gt; &lt;url&gt;https://invalid&lt;/url&gt; &lt;releases&gt;&lt;enabled&gt;true&lt;/enabled&gt;&lt;/releases&gt; &lt;snapshots&gt;&lt;enabled&gt;false&lt;/enabled&gt;&lt;/snapshots&gt; &lt;/pluginRepository&gt; &lt;/pluginRepositories&gt; &lt;/profile&gt; &lt;/profiles&gt; &lt;activeProfiles&gt; &lt;activeProfile&gt;chainguard&lt;/activeProfile&gt; &lt;/activeProfiles&gt; &lt;/settings&gt; Update the repositories block in app/build.gradle to point to Chainguard Libraries. If you are using the remediated repository, set it first.
Note: If your project manages repositories in settings.gradle, add the Chainguard repositories there instead.
repositories { maven { url = uri(&#34;https://libraries.cgr.dev/java-remediated/&#34;) credentials { username = providers.environmentVariable(&#34;CHAINGUARD_JAVA_IDENTITY_ID&#34;).orNull password = providers.environmentVariable(&#34;CHAINGUARD_JAVA_TOKEN&#34;).orNull } } maven { url = uri(&#34;https://libraries.cgr.dev/java/&#34;) credentials { username = providers.environmentVariable(&#34;CHAINGUARD_JAVA_IDENTITY_ID&#34;).orNull password = providers.environmentVariable(&#34;CHAINGUARD_JAVA_TOKEN&#34;).orNull } } mavenCentral() }If a dependency&rsquo;s pom file is found in Chainguard but the JAR is not, Gradle fails rather than falling back to Central automatically. If this happens, check whether a newer version of the dependency is available in Chainguard&rsquo;s catalog and update the version in your build.gradle.
Bazel uses MODULE.bazel to configure repositories via rules_jvm_external. The configuration described on this page uses ~/.netrc, but note that .netrc only supports one set of credentials per hostname. Since all Chainguard Libraries are served from libraries.cgr.dev, configuring .netrc for Java will override credentials for any other ecosystem.
First, set up ~/.netrc with your Chainguard credentials:
printf &#39;machine libraries.cgr.dev\nlogin %s\npassword %s\n&#39; &#34;$CHAINGUARD_JAVA_IDENTITY_ID&#34; &#34;$CHAINGUARD_JAVA_TOKEN&#34; &gt; ~/.netrc chmod 600 ~/.netrc Note: ~/.netrc stores credentials in plaintext on disk. Be careful not to copy .netrc into a project directory where it could be accidentally committed to version control. Use short-lived tokens and rotate them regularly. In CI environments, prefer writing credentials to a temporary .netrc file that is cleaned up after the build, or use your CI platform&rsquo;s secrets management to inject credentials as environment variables instead. If you accidentally expose credentials, delete the exposed token.
Next, update the MODULE.bazel to point to Chainguard Libraries. If you are using the remediated repository, add https://libraries.cgr.dev/java-remediated/ first:
maven.install( artifacts = [ # your artifacts here ], repositories = [ &#34;https://libraries.cgr.dev/java-remediated/&#34;, &#34;https://libraries.cgr.dev/java/&#34;, ], use_credentials_from_home_netrc_file = True, )The use_credentials_from_home_netrc_file = True attribute tells the dependency resolver to read credentials from ~/.netrc when authenticating with repositories. Without this flag, the ~/.netrc is ignored, and requests to Chainguard fail authentication silently.
Repository manager If your organization uses a repository manager, configure it to use Chainguard Libraries as an upstream source first. Follow the global configuration documentation for your repository manager.
Once configured, point your build tool at your repository manager URL. In this setup, the credentials your build tool uses are your repository manager credentials — not a Chainguard pull token.
Create or update ~/.m2/settings.xml to point Maven at your repository manager and override Central with invalid URLs. See example settings files in Chainguard&rsquo;s demo repository on GitHub for different repository managers.
Use a configuration similar to the following. Make sure to update the credentials in the server section to use your repository manager account credentials, using environment variables when possible. This example uses environment variables for Artifactory credentials:
&lt;settings&gt; &lt;mirrors&gt; &lt;mirror&gt; &lt;!-- Set the identifier for the server credentials for repository manager access must match server name in &#34;servers&#34; --&gt; &lt;id&gt;example&lt;/id&gt; &lt;!-- Send all requests to the repository manager URL --&gt; &lt;mirrorOf&gt;*&lt;/mirrorOf&gt; &lt;!-- Ordered repository group with - java-remediated - java - Maven central - anything else needed, including internal repos --&gt; &lt;!--&lt;url&gt;https://repo.example.com:8443/repository/java-all/&lt;/url&gt;--&gt; &lt;url&gt;http://localhost:8081/repository/java-all/&lt;/url&gt; &lt;/mirror&gt; &lt;/mirrors&gt; &lt;!-- Activate repo manager and override central repo from Maven itself with invalid URLs --&gt; &lt;activeProfiles&gt; &lt;activeProfile&gt;repo-manager&lt;/activeProfile&gt; &lt;/activeProfiles&gt; &lt;profiles&gt; &lt;profile&gt; &lt;id&gt;repo-manager&lt;/id&gt; &lt;repositories&gt; &lt;repository&gt; &lt;id&gt;central&lt;/id&gt; &lt;url&gt;http://central&lt;/url&gt; &lt;releases&gt; &lt;enabled&gt;true&lt;/enabled&gt; &lt;/releases&gt; &lt;snapshots&gt; &lt;enabled&gt;true&lt;/enabled&gt; &lt;/snapshots&gt; &lt;/repository&gt; &lt;/repositories&gt; &lt;pluginRepositories&gt; &lt;pluginRepository&gt; &lt;id&gt;central&lt;/id&gt; &lt;url&gt;http://central&lt;/url&gt; &lt;releases&gt; &lt;enabled&gt;true&lt;/enabled&gt; &lt;/releases&gt; &lt;snapshots&gt; &lt;enabled&gt;true&lt;/enabled&gt; &lt;/snapshots&gt; &lt;/pluginRepository&gt; &lt;/pluginRepositories&gt; &lt;/profile&gt; &lt;/profiles&gt; &lt;servers&gt; &lt;server&gt; &lt;id&gt;repo-manager&lt;/id&gt; &lt;username&gt;${env.ARTIFACTORY_USERNAME}&lt;/username&gt; &lt;password&gt;{env.ARTIFACTORY_PASSWORD}&lt;/password&gt; &lt;/server&gt; &lt;/servers&gt; &lt;/settings&gt; Update the repositories block in app/build.gradle to point to your repository manager, removing any direct references to mavenCentral() or other repositories if your repository manager already proxies them:
repositories { maven { url = uri(&#34;https://repo.example.com/java-all/&#34;) credentials { username = providers.environmentVariable(&#34;REPO_MANAGER_USERNAME&#34;).orNull password = providers.environmentVariable(&#34;REPO_MANAGER_PASSWORD&#34;).orNull } } } First, configure your repo manager authentication in ~/.netrc:
machine repo.example.com login YOUR_REPO_MANAGER_USERNAME password YOUR_REPO_MANAGER_PASSWORDThen update MODULE.bazel to point to your repo manager:
maven.install( artifacts = [ # your artifacts here ], repositories = [ &#34;https://repo.example.com/java-all/&#34;, ], use_credentials_from_home_netrc_file = True, ) Replace https://repo.example.com/java-all/ with your repo manager&rsquo;s virtual repository URL, and update the username and password.
Step 4: Refresh dependencies To force re-resolution from Chainguard, you can clear local caches or clear only relevant artifacts.
Remove local cache:
rm -rf ~/.m2/repositoryRemove specific artifact:
rm -rf ~/.m2/repository/&lt;group&gt;/&lt;artifact&gt; Remove local caches and stop any running daemons:
rm -rf ~/.gradle/caches ./gradlew --stop Note: Run ./gradlew --stop after clearing the Gradle cache to stop any running daemons. Skipping this step can cause build errors on the next run.
Remove the Bazel cache, including all downloaded artifacts and build outputs:
bazel clean --expunge Step 5: Build the project Run a full build and capture the output.
cd your-project ./mvnw install 2&gt;&amp;1 | tee /tmp/mvn-output.txtTo check which repositories served completed downloads:
grep &#34;Downloaded from&#34; /tmp/mvn-output.txtAll downloads should come from chainguard or chainguard-remediated.
cd your-project ./gradlew build --info 2&gt;&amp;1 | tee /tmp/gradle-output.txt bazel build //... 2&gt;&amp;1 | tee /tmp/bazel-output.txtBazel embeds the source repository URL in the cache path for each downloaded artifact.
Look for lines beginning with Downloaded from chainguard: or Downloaded from chainguard-remediated: to confirm artifacts are being served by Chainguard.
If all artifacts download from Central, your credentials may be invalid or expired. Regenerate your pull token, re-export the pull token credentials as environment variables, then clear the cache and rebuild.
Step 6: Verify artifacts To check whether a specific artifact was built by Chainguard, use chainctl libraries verify /full/path/to/artifact.jar. Verify artifacts immediately after a clean build, before any repackaging.
To list all downloaded JARs:
find ~/.m2/repository -name &#34;*.jar&#34;Pick any JAR from the output to verify. For example:
chainctl libraries verify \ ~/.m2/repository/org/apache/commons/commons-lang3/3.13.0/commons-lang3-3.13.0.jarA checksum mismatch means the artifact came from Central rather than Chainguard. This can happen if the artifact was cached from a previous build. To resolve, delete the specific artifact and re-download it:
rm ~/.m2/repository/org/apache/commons/commons-lang3/3.13.0/commons-lang3-3.13.0.jar ./mvnw dependency:get -Dartifact=org.apache.commons:commons-lang3:3.13.0Then run chainctl libraries verify again.
Gradle stores JARs in a content-addressed cache. To list all downloaded JARs:
find ~/.gradle/caches/ -name &#34;*.jar&#34;This command returns the full paths of your project dependencies. Choose one of the JARs, and verify it using the full path. For example:
chainctl libraries verify ~/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/20.0/c2ea0b73679bc223a7ab119afd0ece31636173bc/guava-20.0.jarA checksum mismatch means the artifact came from Central rather than Chainguard. This can happen if the artifact was cached from a previous build. To resolve, delete the specific artifact and re-download it. For example:
rm -rf ~/.gradle/caches/modules-2/files-2.1/commons-collections ./gradlew dependenciesThen run chainctl libraries verify again.
Bazel embeds the source repository URL in the cache path, making it easy to identify which JARs came from Chainguard. Use find to locate a JAR. For example:
Linux:
find ~/.cache/bazel -path &#34;*cgr.dev*&#34; -name &#34;jackson-databind*.jar&#34; 2&gt;/dev/nullMacOS:
find ~/Library/Caches/bazel -path &#34;*cgr.dev*&#34; -name &#34;jackson-databind*.jar&#34; 2&gt;/dev/nullThen verify using the full path. For example:
chainctl libraries verify ~/Library/Caches/bazel/_bazel_example/c22a55500...f4231148aeda823%40libraries.cgr.dev/java/com/fasterxml/jackson/core/jackson-databind/2.9.10/jackson-databind-2.9.10.jar A successfully verified artifact produces output similar to:
Artifact: /path/to/artifact.jar Verification Coverage: 100.00%
