You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Suggestion: a "docker pull --dry-run" option #6059
It would be useful in some automation environment to detect if there are new images available without pulling them right away. In my case, the launcher script detects if there is an update, and offers it to the user.
In an ideal world, I would launch docker pull --dry-run and either parse the output or check the return code.
Migrated from this issue, as suggested in the thread to make a feature request in the Docker CLI : distribution/distribution#2412
Workaround:
It is possible to achieve this using image inspection. The following script works in my case but has major flaws : is is slow, will break if the manifest structure changes, and works only because the variant I use happens to be the first in the remote repo's manifest. I feel that there is nothing better that a docker pull to know what a docker pull should be doing, and any scripting attempt like this one is an approximation.
images=$(docker compose config | grep 'image:'| awk '{print $2}')forimagein$images;do# Get the local digest in the format sha256:<hash>
localDigest=$(docker image inspect --format='{{index .Id}}'$image2>/dev/null)if [[ $?-ne 0 ]];thenecho"At least one image not found locally: $image"
updatesFound=true
breakfiecho$localDigest# Get the remote digest in format sha256:<hash># This works ONLY if the desired config is from the first variant# Would have been cleaner with jq but it is not available in a git bash on Windows...
remoteDigest=$(docker manifest inspect -v $image \| awk '/"config":/ {found=1} found&& /"digest"/ {print $2; exit}' \| tr -d '",')echo$remoteDigest# Perform comparisonif [[ "$localDigest"=="$remoteDigest" ]];thenecho"Up to date: $image"elseecho"At least one image outdated: $image"
updatesFound=true
breakfidone
EDIT: Workaround does not work on a mac, as the content of docker image inspect differs.
This is something that has been discussed a few times (and possibly could be considered; it would require changes on the daemon-side though, which is where the actual pull is handled). That said; do you have more context on your use-case; effectively, a docker pull would not pull anything if the image is up-to-date;
docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
Digest: sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
Status: Image is up to date for alpine:latest
docker.io/library/alpine:latest
docker run --pull always --rm alpine echo foo
latest: Pulling from library/alpine
Digest: sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
Status: Image is up to date for alpine:latest
foo
I should note that with the containerd image-store integration enabled;
The digest of the image will now reflect the digests of the "distributed" image, so should match the top-most digest from docker manifest inspect (or `docker buildx imagetools inspect)
docker image ls --no-trunc alpine
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c 2 months ago 12.8MB
docker buildx imagetools inspect alpine | head -n 3
Name: docker.io/library/alpine:latest
MediaType: application/vnd.oci.image.index.v1+json
Digest: sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
do you have more context on your use-case; effectively, a docker pull would not pull anything if the image is up-to-date;
In our context, we have a few services running, that can be started by the users. Most users will know nothing about docker except that it is installed on their machines, the rest is handled by the launcher script.
Why do we want to check for updates, instead of automatically pulling them?
The total download is over 3GB, which can be slow for some of our users with limited bandwidth.
Our containers contain dependencies (R, conda) that are installed at runtime. Installing a single R dependency can take from a few seconds to nearly an hour. While the dependencies were previously stored on volumes to avoid losing them, this caused compatibility problems when the containers updated. Hence, they are now discarded with the container. It is therefore natural to ask them permission before discarding the containers, knowing it can seriously delay their work.
About the containerd solution, that could simplify things a lot! However, since we do not control the client environment, it would be hard to ask every user to switch to this experimental configuration. I just found out that the workaround I had pasted in the original question does not work for mac users, since the content of docker image inspect differs. We'll have to find something else...
On the other hand I tracked the pull calls up to here (I imagine this is the Daemon-side you were referring to). I wanted to see if there was some comparing logic I could pull out, but wasn't successful.
In conclusion, any other workaround ideas are welcome.
Docker 20.10 and newer should cache the manifest after pulling (if the containerd image store is not in use), and skip the actual pull if the content is matched; moby/moby#41607
With the containerd image store, the same should happen (containerd always stores the manifests that it pulled);
With a local registry (to check requests received by the registry) and the containerd image store enabled;
docker run -d --name registry --rm -p 5002:5000 registry:2
echo -e 'FROM alpine'| docker build -t host.docker.internal:5002/myimage:latest -
docker push host.docker.internal:5002/myimage:latest
The push refers to repository [host.docker.internal:5002/myimage]
6e771e15690e: Pushed
109631c1e18d: Pushed
latest: digest: sha256:74dbc6645ece3658846442acfeffa07d7cd0c8d0f7ca736731037a0fcce380f9 size: 855
docker rmi host.docker.internal:5002/myimage:latest
Untagged: host.docker.internal:5002/myimage:latest
Deleted: sha256:74dbc6645ece3658846442acfeffa07d7cd0c8d0f7ca736731037a0fcce380f9
docker pull host.docker.internal:5002/myimage:latest
latest: Pulling from myimage
Digest: sha256:74dbc6645ece3658846442acfeffa07d7cd0c8d0f7ca736731037a0fcce380f9
Status: Downloaded newer image for host.docker.internal:5002/myimage:latest
host.docker.internal:5002/myimage:latest
docker pull host.docker.internal:5002/myimage:latest
latest: Pulling from myimage
Digest: sha256:74dbc6645ece3658846442acfeffa07d7cd0c8d0f7ca736731037a0fcce380f9
Status: Image is up to date for host.docker.internal:5002/myimage:latest
host.docker.internal:5002/myimage:latest
docker pull host.docker.internal:5002/myimage:latest
# repeat a few times
Logs for registry container shows that the pulls only result in a HEAD request, with no GET request (as the image wasn't updated);
After disabling the containerd image store, and repeating the same;
docker run -d --name registry --rm -p 5002:5000 registry:2
echo -e 'FROM alpine'| docker build -t host.docker.internal:5002/myimage:latest -
docker push host.docker.internal:5002/myimage:latest
The push refers to repository [host.docker.internal:5002/myimage]
1231a673589a: Pushed
latest: digest: sha256:5f24a984dc6d405d4832032310023f8033aab72ee6bd62582e446b8aafd8584f size: 527
docker rmi host.docker.internal:5002/myimage:latest
Untagged: host.docker.internal:5002/myimage:latest
Untagged: host.docker.internal:5002/myimage@sha256:5f24a984dc6d405d4832032310023f8033aab72ee6bd62582e446b8aafd8584f
Deleted: sha256:4b071331304977d08f6ff896f2ece366bfaa03f0c1729fa2b4331d477405140b
docker pull host.docker.internal:5002/myimage:latest
latest: Pulling from myimage
d69d4d41cfe2: Already exists
Digest: sha256:5f24a984dc6d405d4832032310023f8033aab72ee6bd62582e446b8aafd8584f
Status: Downloaded newer image for host.docker.internal:5002/myimage:latest
host.docker.internal:5002/myimage:latest
docker pull host.docker.internal:5002/myimage:latest
latest: Pulling from myimage
Digest: sha256:5f24a984dc6d405d4832032310023f8033aab72ee6bd62582e446b8aafd8584f
Status: Image is up to date for host.docker.internal:5002/myimage:latest
host.docker.internal:5002/myimage:latest
# repeat a few times
Logs show that only HEAD requests are made for the manifest (the GET requests on the /v2/ endpoint are for feature discovery and don't count towards rate limits);
Thanks for suggesting --dry-run@jmlord, this would be such a helpful feature for smaller setups in a time where zero- or subzero-day exploits are raining down permanently due to the well known changes in how attacks are being carried out nowadays. And thanks a lot for the insights @thaJeztah, obviously, it's a textbook example for a simple feature (from a user perspective) to be way more complex (from a dev perspective). Still hope it will land in some form at some point. 🙏
docker pull --dry-run <image> asks the daemon to resolve the reference to a digest in the registry (via the existing /distribution/{name}/json endpoint, so no daemon changes are needed) and compares it against the local image's RepoDigests. Nothing is downloaded. It reports one of:
Status: Image is up to date for <ref>
Status: A newer image is available for <ref>
Status: Image <ref> is not available locally and would be pulled
It can't be combined with --all-tags. @thaJeztah, since you mentioned this might need daemon-side work, I'd appreciate your thoughts on whether the client-side approach is acceptable.
Activity
thaJeztah commentedon May 13, 2025
This is something that has been discussed a few times (and possibly could be considered; it would require changes on the daemon-side though, which is where the actual pull is handled). That said; do you have more context on your use-case; effectively, a
docker pullwould not pull anything if the image is up-to-date;I should note that with the containerd image-store integration enabled;
The digest of the image will now reflect the digests of the "distributed" image, so should match the top-most digest from
docker manifest inspect(or `docker buildx imagetools inspect)docker image ls --no-trunc alpine REPOSITORY TAG IMAGE ID CREATED SIZE alpine latest sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c 2 months ago 12.8MB docker buildx imagetools inspect alpine | head -n 3 Name: docker.io/library/alpine:latest MediaType: application/vnd.oci.image.index.v1+json Digest: sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88cjmlord commentedon May 14, 2025
Hi Sebastiaan,
Thanks for your reply,
In our context, we have a few services running, that can be started by the users. Most users will know nothing about docker except that it is installed on their machines, the rest is handled by the launcher script.
Why do we want to check for updates, instead of automatically pulling them?
I therefore added a the above digest checking procedure to our launcher script
prod-server.sh, currently in a branch here: https://github.com/GEO-BON/bon-in-a-box-pipeline-engine/tree/mamba_update_stagingI am wary though that this can break at any moment with updates from the docker CLI tools.
jmlord commentedon May 28, 2025
Hi again,
About the containerd solution, that could simplify things a lot! However, since we do not control the client environment, it would be hard to ask every user to switch to this experimental configuration. I just found out that the workaround I had pasted in the original question does not work for mac users, since the content of
docker image inspectdiffers. We'll have to find something else...On the other hand I tracked the pull calls up to here (I imagine this is the Daemon-side you were referring to). I wanted to see if there was some comparing logic I could pull out, but wasn't successful.
In conclusion, any other workaround ideas are welcome.
thaJeztah commentedon Jun 2, 2025
Docker 20.10 and newer should cache the manifest after pulling (if the containerd image store is not in use), and skip the actual pull if the content is matched; moby/moby#41607
With the containerd image store, the same should happen (containerd always stores the manifests that it pulled);
With a local registry (to check requests received by the registry) and the containerd image store enabled;
Logs for
registrycontainer shows that the pulls only result in aHEADrequest, with noGETrequest (as the image wasn't updated);After disabling the containerd image store, and repeating the same;
Logs show that only
HEADrequests are made for the manifest (theGETrequests on the/v2/endpoint are for feature discovery and don't count towards rate limits);jmlord commentedon Jun 2, 2025
Hi @thaJeztah ,
Did you want to post this here on on this other issue: #6082 ?
In my case, this doesn't help, since I want to detect the update without updating right away...
thaJeztah commentedon Jun 2, 2025
DOH! You're right; I think I posted it in the wrong tab! 🙈
Let me port it in the other ticket 😅
svoop commentedon Sep 6, 2026
Thanks for suggesting
--dry-run@jmlord, this would be such a helpful feature for smaller setups in a time where zero- or subzero-day exploits are raining down permanently due to the well known changes in how attacks are being carried out nowadays. And thanks a lot for the insights @thaJeztah, obviously, it's a textbook example for a simple feature (from a user perspective) to be way more complex (from a dev perspective). Still hope it will land in some form at some point. 🙏Add --dry-run flag to docker pull
raphgm commentedon Oct 13, 2026
I've opened a PR implementing this: #7379
docker pull --dry-run <image>asks the daemon to resolve the reference to a digest in the registry (via the existing/distribution/{name}/jsonendpoint, so no daemon changes are needed) and compares it against the local image'sRepoDigests. Nothing is downloaded. It reports one of:Status: Image is up to date for <ref>Status: A newer image is available for <ref>Status: Image <ref> is not available locally and would be pulledIt can't be combined with
--all-tags. @thaJeztah, since you mentioned this might need daemon-side work, I'd appreciate your thoughts on whether the client-side approach is acceptable.Add --dry-run flag to docker pull
Add --dry-run flag to docker pull
Add --dry-run flag to docker pull