Kubernetes Error Guide: 'manifest unknown' Image Tag or Digest Not in Registry
Fix 'manifest unknown' in Kubernetes: the image tag or digest your pod references does not exist in the registry — find the missing tag and repoint it.
- #kubernetes-helm
- #troubleshooting
- #errors
- #registry
Exact Error Message
When the kubelet asks a registry for an image tag or digest that the registry does not have, the pull fails and the pod surfaces a manifest unknown error inside an ErrImagePull/ImagePullBackOff loop:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulling 18s (x3 over 51s) kubelet Pulling image "ghcr.io/acme/api:v1.9.3"
Warning Failed 17s (x3 over 50s) kubelet Failed to pull image "ghcr.io/acme/api:v1.9.3": rpc error: code = NotFound desc = failed to pull and unpack image "ghcr.io/acme/api:v1.9.3": failed to resolve reference "ghcr.io/acme/api:v1.9.3": ghcr.io/acme/api:v1.9.3: not found
Warning Failed 17s (x3 over 50s) kubelet Error: ErrImagePull
Normal BackOff 4s (x5 over 49s) kubelet Back-off pulling image "ghcr.io/acme/api:v1.9.3"
Pulling the same reference directly with the Docker or containerd CLI returns the canonical wording:
Error response from daemon: manifest unknown
The key signal is not found / manifest unknown: the registry, repository, and your credentials are all fine — the specific tag or digest simply is not there.
What the Error Means
A container image reference has three parts: a registry host, a repository path, and a tag or digest (registry/repo:tag or registry/repo@sha256:...). When the kubelet pulls, the registry first resolves the tag or digest to a manifest — the JSON document that lists the image’s layers and config. manifest unknown means the registry successfully received your request and authorized it, but has no manifest matching that exact tag or digest.
This is fundamentally different from authentication or repository-existence failures. A pull access denied or repository does not exist error means the registry rejected you before resolving the tag. manifest unknown means you got far enough that the registry looked up the tag and came back empty. In practice the repository exists and you can read it — the version you named was never pushed, was deleted, or was overwritten.
Common Causes
- Typo or wrong tag —
v1.9.3was requested but the registry only hasv1.9.2and1.9.3(novprefix). - Tag never pushed — CI built and tagged the image but the push step failed or was skipped, so the tag does not exist remotely.
- Tag deleted or expired — a registry retention/garbage-collection policy pruned older tags, or someone deleted the tag.
- Digest no longer present — a pinned
@sha256:digest was garbage-collected after the tag moved to a new build. - Wrong architecture in a single-arch image — a multi-arch manifest list is missing the node’s platform (e.g.
arm64), so resolution for that platform returns nothing. - Wrong repository path —
acme/apivsacme/api-service; the path resolves to a real repo but not the one holding your tag. - Mutable
latestrace —latestwas retagged to a new digest and the old one pruned mid-rollout.
How to Reproduce the Error
Reference a tag that does not exist in a public repository:
apiVersion: v1
kind: Pod
metadata:
name: manifest-unknown-demo
spec:
containers:
- name: app
image: registry.k8s.io/pause:does-not-exist
kubectl apply -f manifest-unknown-demo.yaml
kubectl get pod manifest-unknown-demo
NAME READY STATUS RESTARTS AGE
manifest-unknown-demo 0/1 ErrImagePull 0 12s
kubectl describe pod manifest-unknown-demo shows the manifest unknown / not found message because the pause repository exists but the does-not-exist tag does not.
Diagnostic Commands
# Read the exact pull error and the image reference the pod uses
kubectl describe pod <POD> | grep -A8 Events
kubectl get pod <POD> -o jsonpath='{.spec.containers[*].image}{"\n"}'
# List the tags that actually exist in the repository (no pull, read-only)
crane ls ghcr.io/acme/api
skopeo list-tags docker://ghcr.io/acme/api
# Inspect a specific tag's manifest without pulling layers
crane manifest ghcr.io/acme/api:v1.9.3
skopeo inspect --raw docker://ghcr.io/acme/api:v1.9.3
# Check which platforms a multi-arch tag publishes
crane manifest ghcr.io/acme/api:v1.9.3 | jq '.manifests[].platform'
# Confirm the node architecture the tag must cover
kubectl get node <NODE> -o jsonpath='{.status.nodeInfo.architecture}{"\n"}'
crane ls (or skopeo list-tags) is the decisive check: if your tag is absent from that list, the manifest genuinely does not exist.
Step-by-Step Resolution
1. Read the exact reference. Copy the full registry/repo:tag string from the describe output. Watch for a missing or extra v prefix and subtle path differences.
2. List the real tags. Run crane ls <repo> or skopeo list-tags docker://<repo>. Confirm whether your tag is present at all.
3. If the tag is missing, repoint to one that exists. Update the deployment to a known-good tag:
kubectl set image deployment/api app=ghcr.io/acme/api:v1.9.2
4. If the tag should exist, fix the publish step. Re-run the CI job’s build-and-push, and verify the push succeeded by re-listing tags. A common cause is a push that failed silently after a successful build.
5. For digest pins, re-resolve the digest. If you pin @sha256:... and it was garbage-collected, get the current digest and update the manifest:
crane digest ghcr.io/acme/api:v1.9.3
# -> sha256:abcd... use this value in the image reference
6. For architecture mismatches, publish the missing platform. Rebuild as a multi-arch image with docker buildx build --platform linux/amd64,linux/arm64 --push, then confirm with crane manifest that the node’s platform is listed.
7. Avoid mutable tags in production. Pin to immutable digests or versioned tags so a retag/prune elsewhere cannot pull the manifest out from under a running rollout.
Prevention and Best Practices
- Enable immutable tags on your registry so a published version can never be overwritten or pruned out from under deployments.
- Gate deploys on a registry existence check (
crane manifest/skopeo inspect) in CI before applying the manifest to the cluster. - Pin production workloads to digests, not floating tags like
latest, so the reference is content-addressable and stable. - Align retention policies with your rollback window: never prune tags that any live or recently-live Deployment still references.
- Standardize tag naming (always-with-
vor always-without) across CI and manifests to eliminate prefix typos. - For multi-arch fleets, always build and push every node architecture in the cluster.
Related Errors
- pull access denied — the repository exists but auth fails, a different failure mode than a missing tag.
- ErrImagePull — the umbrella pull failure that
manifest unknowntriggers. - ImagePullBackOff — the back-off state after repeated pull failures.
- Failed to pull image — broader pull-failure triage.
Frequently Asked Questions
How is manifest unknown different from repository does not exist? repository does not exist (or pull access denied) fails before tag resolution — the registry rejects the repo path or your credentials. manifest unknown means the repo is reachable and authorized, but the specific tag or digest you named has no manifest.
The tag worked yesterday and fails today — what changed? Almost always a registry retention/garbage-collection job pruned the tag, or someone retagged a mutable tag (latest) and the old manifest was cleaned up. List current tags with crane ls to confirm, and pin to immutable digests to prevent recurrence.
Can credentials cause manifest unknown? Not directly. With bad or missing credentials you get pull access denied instead. If you only have read access to some tags, though, a tag outside your scope can appear as not found — verify with a skopeo inspect using the same credentials.
My image is multi-arch and only fails on some nodes. Why? The manifest list is missing those nodes’ platform. The kubelet resolves the manifest for the node’s architecture; if arm64 is absent, that node reports the tag as not found while amd64 nodes pull fine. Rebuild with all required platforms.
Should I use tags or digests in production? Digests. A @sha256: reference is immutable and content-addressed, so it cannot be silently retagged or pruned mid-rollout. Tags are convenient for humans but should be resolved to digests at deploy time.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.