Skip to content

[Bug]: docker pull specific tag will trigger sync even if images exists locally #3724

@TeCHiScy

Description

@TeCHiScy

zot version

v2.1.13

Describe the bug

For example, when I use docker pull zot.mydomain.com/repo1/image1:latest to pull image1 from zot registry, the log at zot shows that it tries to sync image from external sources, even if /repo1/image1:latest locally exists.

│ {"time":"2026-01-21T02:50:05.611280445Z","level":"info","message":"trying to get updated image by syncing on demand","repository":"repo1/image1","referenc │
│ e":"latest","caller":"zotregistry.dev/zot/v2/pkg/api/routes.go:2182","func":"zotregistry.dev/zot/v2/pkg/api.getImageManifest","goroutine":3810}             │
│ {"time":"2026-01-21T02:50:05.611345174Z","level":"debug","message":"starting on-demand image sync","repo":"repo1/image1","reference":"latest","serviceID": │
│ 0,"timeout":10800000000000,"caller":"zotregistry.dev/zot/v2/pkg/extensions/sync/on_demand.go:193","func":"zotregistry.dev/zot/v2/pkg/extensions/sync.(*Base │
│ OnDemand).syncImage","goroutine":3807}                                                                                                                      │
│ {"time":"2026-01-21T02:50:05.611387652Z","level":"info","message":"will not sync image, filtered out by content","remote":"registry-1.docker.io","repo":"de │
│ vops/k8sctl","reference":"latest","caller":"zotregistry.dev/zot/v2/pkg/extensions/sync/service.go:306","func":"zotregistry.dev/zot/v2/pkg/extensions/sync.( │
│ *BaseService).SyncImage","goroutine":3807}                                                                                                                  │
│ {"time":"2026-01-21T02:50:05.611425932Z","level":"error","message":"failed to sync image","error":"image is filtered out by sync config","repository":"devo │
│ ps/k8sctl","reference":"latest","caller":"zotregistry.dev/zot/v2/pkg/api/routes.go:2186","func":"zotregistry.dev/zot/v2/pkg/api.getImageManifest","goroutin │
│ e":3810}

The reason is that getImageManifest preferentially uses godigest to check if reference is of format sha256:xxxxxx. But in this case, it is actually latest. This will cause godigest to fail, and fallback to sync externally, if sync extension was enabled.

zot/pkg/api/routes.go

Lines 2172 to 2180 in 088914b

_, digestErr := godigest.Parse(reference)
if digestErr == nil {
// if it's a digest then return local cached image, if not found and sync enabled, then try to sync
content, digest, mediaType, err := imgStore.GetImageManifest(name, reference)
if err == nil || !syncEnabled {
return content, digest, mediaType, err
}
}

Currently, my workaround is to configure allow lists in sync extension via contents, so that to avoid local (also private) images to trigger sync. Then, as the logic goes on, it will fallbacks (again) to find the referenced image locally (which will success), as in:

return imgStore.GetImageManifest(name, reference)

In my use case, the workaround is not convience, because I want zot to be a proxy registry for all images that not exists locally (private). This can not be configured via contents since it only supports glob syntax, which is essentially a allowlist.

Moreover, when local images was triggered to sync externally, it will introduce non-negligible latency in docker pull (also kubelet pull) operation, even causes k8s ImagePullBackOff, when network condition to upstream registry was poor.

My thought on this issue is:

  • Introduce some mechanism to exclude repos in sync extension, or,
  • Find reference locally before triggers sync (i.e., not validate digest via godiest preferentially). If needed, a configuration can also be added for backward compatibility.

In conclude, my question is:

  • Is this behavior by design?
  • If there's any suggestion to improve the behavior, I can take time to draft a PR.

To reproduce

  1. Configuration
  2. Client tool used
  3. Seen error

Expected behavior

No response

Screenshots

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrm-externalRoadmap item submitted by non-maintainers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions