Artifactory plugin Docker push fails with "Authentication is required" on agents using the containerd image store

Last Reviewed:2026-10-19()
3 minute readKnowledge base

Issue

A Pipeline using the Artifactory Plugin's Docker push step (for example Artifactory.docker().push(), or the dockerPushStep/DockerArtifactoryUpload build step) fails intermittently with an authentication error, even though the configured "Artifactory Server" credential is correct:

INFO: Pushing image: artifactory.example.com:9999/example-repo/example-image:1.0.0 com.github.dockerjava.api.exception.DockerClientException: Could not push image: error from registry: Authentication is required at com.github.dockerjava.core.command.PushImageResultCallback.throwFirstError(PushImageResultCallback.java:42) at com.github.dockerjava.api.async.ResultCallbackTemplate.awaitCompletion(ResultCallbackTemplate.java:93) at org.jfrog.build.extractor.docker.DockerJavaWrapper.pushImage(DockerJavaWrapper.java:42) at org.jfrog.build.extractor.docker.extractor.DockerPush.execute(DockerPush.java:85) at org.jfrog.build.extractor.packageManager.PackageManagerExtractor.executeAndSaveBuildInfo(PackageManagerExtractor.java:33) at org.jfrog.build.extractor.docker.extractor.DockerPush.main(DockerPush.java:70)
▼

A variant of the same underlying failure reports a 401 while fetching a registry token instead:

com.github.dockerjava.api.exception.DockerClientException: Could not push image: failed to authorize: failed to fetch anonymous token: unexpected status from GET request to https://artifactory.example.com/v2/token?scope=repository:example-repo:pull,push&service=...: 401
▼

The same job, with the same Jenkins credentials, succeeds when it runs on one agent and fails with this error when it runs on another. Editing ~/.docker/config.json on the affected agents has no effect on the outcome.

Environment

Hypothesis

The Artifactory plugin’s Docker push step does not perform docker push through the agent’s own Docker CLI or read ~/.docker/config.json. It spawns a dedicated child JVM (visible in the build log as java -cp …​/artifactory/4.0.8/* org.jfrog.build.extractor.docker.extractor.DockerPush) that talks to the Docker Engine API directly using the docker-java library:

  • DockerPushExecutor.execute() launches that child process.

  • DockerPush.execute() reads the username and password from the Jenkins "Artifactory Server" credential and passes them straight into DockerJavaWrapper.pushImage().

  • DockerJavaWrapper.pushImage() builds a com.github.dockerjava.api.model.AuthConfig from only the username and password, with no registry address, and calls dockerClient.pushImageCmd(imageTag).withAuthConfig(authConfig).

Because ~/.docker/config.json is never read in this code path, editing it on the agent has no effect, regardless of what it contains.

An AuthConfig built without a registry address is exactly what docker login on the CLI also produces when a registry is not explicitly specified, and Docker Engine has historically been lenient about resolving it anyway. Docker Engine’s newer containerd image store handles registry-auth resolution differently, and does not resolve this incomplete AuthConfig the same way the classic overlay2 storage driver does, so the push fails with the authentication errors above. The classic overlay2 driver is unaffected. Per Docker’s own documentation, the containerd image store is the default on fresh Docker Engine 29.0+ installations, while hosts upgraded from an earlier Engine version keep using overlay2 unless explicitly switched over. This could explain why the same job can succeed or fail depending only on which agent host it lands on: agent pools that mix freshly-provisioned hosts with older, upgraded hosts have a mix of both storage drivers, and jobs land on different hosts across runs. But we have correlation, not evidence as the explanation might lay deeper in the containerd engine.

This code path in DockerJavaWrapper.pushImage() has not changed since December 2021, and is unmodified on the build-info library’s current default branch at the time of writing.

Workaround

No fix is available from JFrog for this defect as of this writing. Until one ships, disable the containerd image store on any build agent that runs the Artifactory plugin’s Docker push step.

  1. On the affected Docker Engine host, add the following to /etc/docker/daemon.json:

    { "features": { "containerd-snapshotter": false } }
    ▼
  2. Restart the Docker daemon:

    sudo systemctl restart docker
    ▼
  3. Confirm the storage driver is now overlay2:

    docker info | grep "Storage Driver"
    ▼
Apply this to every host in an agent pool, not just the host that most recently failed. Agent pools are commonly recycled across hosts, so a pool with a mix of storage drivers will keep producing intermittent failures until every host in the pool is corrected.
Golden images or provisioning automation for build agents should set this daemon.json setting at provisioning time, rather than relying on a manual one-off change that will not survive the next time the host is recycled or re-provisioned.
This article is part of our Knowledge Base and is provided for guidance-based purposes only. The solutions or workarounds described here are not officially supported by CloudBees and may not be applicable in all environments. Use at your own discretion, and test changes in a safe environment before applying them to production systems.