Skip to content

Fix: Docker Pull Error – Image Not Found or Manifest Unknown

FixDevs ·

Quick Answer

How to fix Docker errors like 'manifest for image not found', 'repository does not exist', or 'pull access denied' when pulling or running images.

The Error

You try to pull or run a Docker image and get one of these errors:

$ docker pull myapp:latest
Error response from daemon: manifest for myapp:latest not found: manifest unknown: manifest unknown

Or when running a container directly:

$ docker run nginx:1.25.99
Unable to find image 'nginx:1.25.99' locally
docker: Error response from daemon: manifest for nginx:1.25.99 not found: manifest unknown: manifest unknown.

Or the error mentions the repository itself:

$ docker pull mycompany/backend:v2
Error response from daemon: pull access denied for mycompany/backend, repository does not exist or may require 'docker login' to access

Or you see a platform-related variant:

$ docker pull --platform linux/amd64 someimage:latest
Error response from daemon: no matching manifest for linux/amd64 in the manifest list entries

All of these share the same root cause: Docker cannot find the image you asked for. The reason varies — it could be a typo, a missing tag, an authentication problem, a platform mismatch, or a rate limit — but the outcome is the same. Docker refuses to pull the image and your workflow stops.

Why This Happens

When you run docker pull or docker run, Docker contacts a container registry (Docker Hub by default) and requests a specific image by name and tag. The registry looks up the image manifest — a JSON document that describes the image layers and their checksums. If the registry cannot find a matching manifest, it returns an error.

Here is what can go wrong at each step:

  • The image name is misspelled. A single typo in the repository name or namespace means Docker asks for an image that does not exist. docker pull ngnix instead of docker pull nginx will fail.
  • The tag does not exist. You requested a specific version tag that was never pushed, has been deleted, or was renamed. Tags like latest are not guaranteed to exist on every repository.
  • The image is in a private registry. The image exists but requires authentication. Without docker login, the registry denies access and Docker reports the repository as nonexistent.
  • Platform mismatch. The image exists but was only built for a different CPU architecture. An ARM64 image pulled on an AMD64 machine (or vice versa) will fail if no matching manifest entry exists.
  • Docker Hub rate limiting. Docker Hub enforces pull rate limits for unauthenticated and free-tier users. When you exceed the limit, pulls fail with opaque error messages that can look like the image does not exist.
  • The image was deprecated or removed. Maintainers sometimes delete old images or rename repositories. An image that worked last month may no longer exist.
  • Registry mirror misconfiguration. If your Docker daemon is configured to use a registry mirror, and that mirror does not have the image cached, the pull can fail even though the image exists on the upstream registry.

Fix 1: Check for Typos in the Image Name

This is the most common cause. A misspelled image name or namespace will always produce a “not found” error.

Verify the exact image name on Docker Hub:

docker search nginx

This searches Docker Hub for images matching the query. Check the output for the correct repository name.

Common typos:

# Wrong
docker pull ngnix
docker pull postgress
docker pull node:latests
docker pull ubunt:22.04

# Correct
docker pull nginx
docker pull postgres
docker pull node:latest
docker pull ubuntu:22.04

Check the namespace. Official images on Docker Hub have no namespace prefix (nginx, postgres, node). Community and organization images have a namespace (mycompany/myapp, bitnami/redis). Omitting or misspelling the namespace causes a “not found” error.

# Wrong -- missing namespace
docker pull myapp

# Correct -- with namespace
docker pull mycompany/myapp:v1.2.0

Check for invisible characters. If you copied the image name from a web page, Slack, or a PDF, it might contain invisible Unicode characters (zero-width spaces, non-breaking spaces, smart quotes). Retype the command manually rather than pasting.

Real-world scenario: You copy a docker pull command from a Slack message and it fails with “manifest unknown.” The issue is an invisible Unicode zero-width space character that got inserted by Slack’s rich text formatting. Retype the command manually instead of pasting.

Fix 2: Verify the Tag Exists

The image name might be correct but the tag might not exist. Tags are mutable — maintainers can delete, rename, or overwrite them at any time.

List available tags for an image on Docker Hub:

# Using Docker Hub API
curl -s "https://hub.docker.com/v2/repositories/library/nginx/tags/?page_size=20" | python3 -m json.tool | grep '"name"'

Or visit https://hub.docker.com/_/nginx/tags in a browser for official images, or https://hub.docker.com/r/namespace/image/tags for community images.

Common tag mistakes:

# Wrong -- this version does not exist
docker pull node:21.0.0

# Correct -- use a version that was actually published
docker pull node:21

# Wrong -- "latest" was not pushed for this image
docker pull mycompany/tool:latest

# Correct -- use an explicit version
docker pull mycompany/tool:v2.3.1

Avoid relying on latest. Not every image has a latest tag. Even when it exists, it may not point to the newest version. Always use explicit version tags in Dockerfiles and Compose files. If your Compose file references a missing tag and containers fail to start, see Fix: Docker Compose Up Common Errors for additional troubleshooting.

Fix 3: Log In to the Registry

If the image is in a private repository, Docker cannot pull it without authentication. The error message “repository does not exist or may require ‘docker login’” is Docker’s way of telling you this.

Log in to Docker Hub:

docker login

Enter your Docker Hub username and password (or personal access token). Docker stores the credentials in ~/.docker/config.json.

Log in to a private registry:

# AWS ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com

# Google Container Registry / Artifact Registry
gcloud auth configure-docker

# GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin

# Azure Container Registry
az acr login --name myregistry

# Generic private registry
docker login registry.example.com

Verify you are logged in:

cat ~/.docker/config.json

Look for an entry matching the registry hostname under "auths". If it is missing, your login did not succeed.

In CI/CD pipelines, authentication failures are especially common because credentials are passed through environment variables that may be empty or expired. Double-check that your CI secrets are populated and have not expired. If you are working with AWS services and hitting access issues, see Fix: AWS S3 Access Denied (403 Forbidden) for related credential troubleshooting.

Use a credential helper instead of storing passwords in plain text:

{
  "credHelpers": {
    "123456789.dkr.ecr.us-east-1.amazonaws.com": "ecr-login",
    "gcr.io": "gcloud",
    "ghcr.io": "gh"
  }
}

Add this to ~/.docker/config.json. Credential helpers fetch fresh tokens automatically and are more secure than storing passwords.

Fix 4: Fix Platform Mismatch (ARM vs AMD64)

With the rise of Apple Silicon (M1/M2/M3/M4) and ARM-based cloud instances (AWS Graviton, Ampere), platform mismatches are increasingly common. An image built for linux/amd64 will not run on linux/arm64 unless a multi-architecture manifest exists.

Check what platform you are running:

docker info --format '{{.Architecture}}'

Or:

uname -m
  • x86_64 = AMD64
  • aarch64 or arm64 = ARM64

Check which platforms an image supports:

docker manifest inspect nginx:latest

Look for the platform entries in the output. If your architecture is not listed, the image does not support your platform.

Pull for a specific platform:

docker pull --platform linux/amd64 myapp:latest

On Apple Silicon, Docker Desktop can run AMD64 images through emulation (QEMU), but this is slower than native ARM64 images. You will see a warning:

WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested

Build multi-architecture images with docker buildx:

docker buildx create --name multiarch --use
docker buildx build --platform linux/amd64,linux/arm64 -t mycompany/myapp:v1.0 --push .

This builds the image for both platforms and pushes a multi-arch manifest to the registry. Anyone pulling the image will automatically get the correct variant for their architecture.

In Docker Compose, specify the platform:

services:
  myapp:
    image: myapp:latest
    platform: linux/amd64

This forces Compose to pull the AMD64 variant even on an ARM64 host. If containers fail to connect to each other after resolving the platform issue, see Fix: Docker Container Not Connecting to Network or Other Containers.

Fix 5: Handle Docker Hub Rate Limits

Docker Hub enforces pull rate limits:

  • Anonymous users: 100 pulls per 6 hours per IP address
  • Authenticated free users: 200 pulls per 6 hours
  • Docker Pro/Team/Business: Higher or unlimited

When you hit the rate limit, Docker returns errors that can be confusing:

Error response from daemon: toomanyrequests: You have reached your pull rate limit.

Or sometimes the error looks like a generic “manifest unknown” instead.

Check your current rate limit status:

TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/nginx:pull" | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
curl -sI -H "Authorization: Bearer $TOKEN" "https://registry-1.docker.io/v2/library/nginx/manifests/latest" | grep -i ratelimit

This shows your remaining pull allowance.

Fix: Authenticate to increase your limit:

docker login

Authenticated pulls get a higher limit. Even a free account doubles your allowance.

Fix: Use a registry mirror or cache.

Set up a pull-through cache to avoid hitting Docker Hub directly:

{
  "registry-mirrors": ["https://mirror.example.com"]
}

Add this to /etc/docker/daemon.json (Linux) or configure it through Docker Desktop settings. A pull-through cache stores images locally after the first pull, so subsequent pulls do not count against your Docker Hub rate limit.

In CI/CD, rate limits are a constant issue because CI runners share IP addresses. Solutions:

  • Log in with a Docker Hub account in your CI pipeline
  • Use a local registry mirror (e.g., Harbor, Nexus, or a Docker Registry pull-through cache)
  • Copy frequently used base images to your own private registry (ECR, GCR, ACR)
# Copy an image to your private registry to avoid Docker Hub limits
docker pull nginx:1.27
docker tag nginx:1.27 123456789.dkr.ecr.us-east-1.amazonaws.com/nginx:1.27
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/nginx:1.27

Fix 6: Handle Deprecated or Removed Images

Image maintainers sometimes remove old versions, rename repositories, or deprecate entire images. An image reference that worked six months ago may no longer resolve.

Common examples:

  • Docker official images sometimes drop old OS versions (e.g., python:3.6 was removed after Python 3.6 reached end of life).
  • Organizations rename repositories (e.g., mycompany/old-name becomes mycompany/new-name).
  • Community images get abandoned and eventually deleted by Docker Hub.

Check if the image was recently removed:

Visit the Docker Hub page for the image. If it returns a 404, the image was deleted.

Check the image’s documentation or changelog for deprecation notices or migration instructions. Most maintainers announce removals in advance.

Pin to a specific digest instead of a tag to avoid surprises:

FROM nginx@sha256:abc123def456...

A digest is immutable — it always refers to the exact same image layers. Unlike tags, which can be moved or deleted, a digest will work as long as the image data exists on the registry. The downside is that you must manually update the digest when you want a newer version.

Mirror critical base images to your own registry so you are not dependent on upstream availability:

# Pull and push to your private registry
docker pull python:3.12-slim
docker tag python:3.12-slim registry.example.com/base/python:3.12-slim
docker push registry.example.com/base/python:3.12-slim

Then reference the mirrored image in your Dockerfiles. If disk space becomes a concern when mirroring multiple images, see Fix: Docker no space left on device for cleanup strategies.

Fix 7: Fix Registry Mirror Configuration

If your Docker daemon is configured to use a registry mirror and that mirror is misconfigured, down, or does not have the image, pulls will fail.

Check your current mirror configuration:

docker info | grep -A 5 "Registry Mirrors"

Or check the daemon configuration file directly:

cat /etc/docker/daemon.json

Look for the registry-mirrors key:

{
  "registry-mirrors": ["https://mirror.example.com"]
}

Test if the mirror is reachable:

curl -s https://mirror.example.com/v2/ | head

A working registry returns {} or a JSON response. If it times out or returns an error, the mirror is the problem.

Fix: Remove the broken mirror temporarily:

Edit /etc/docker/daemon.json and remove or replace the registry-mirrors entry, then restart Docker:

sudo systemctl restart docker

On Docker Desktop, go to Settings > Docker Engine and edit the JSON configuration.

Fix: Add a fallback. Docker does not automatically fall back to Docker Hub if a mirror fails (behavior varies by Docker version). If your mirror is unreliable, remove it from the daemon configuration and set up a proper pull-through cache instead:

docker run -d -p 5000:5000 --name registry-cache \
  -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
  registry:2

Then point your daemon at this local cache:

{
  "registry-mirrors": ["http://localhost:5000"]
}

This cache stores images after the first pull and serves them locally for subsequent pulls. If the cache is empty, it transparently proxies the request to Docker Hub.

Fix 8: Multi-Arch Manifest Issues

Some image pull failures are caused by incomplete or malformed multi-architecture manifests. This is especially common with images that were recently updated to support multiple architectures or were built with misconfigured CI pipelines.

Inspect the manifest to see what is available:

docker manifest inspect --verbose mycompany/myapp:v1.0

This shows every platform variant and their individual digests. If your platform is missing, the image was not built for your architecture.

Force pull by digest to bypass manifest issues:

# Get the digest for a specific platform from the manifest inspect output
docker pull mycompany/myapp@sha256:abc123def456...

If you maintain the image, ensure your build pipeline produces manifests for all required platforms. A common mistake is building multi-arch images with docker buildx but forgetting to include --push or using --load instead (which only supports single-platform):

# Wrong -- --load only supports one platform
docker buildx build --platform linux/amd64,linux/arm64 --load -t myapp .

# Correct -- push the multi-arch manifest to a registry
docker buildx build --platform linux/amd64,linux/arm64 --push -t mycompany/myapp:v1.0 .

Verify the manifest was created correctly:

docker manifest inspect mycompany/myapp:v1.0

You should see entries for each platform you built. If only one platform appears, the build pipeline needs fixing.

If you are building images and the build itself fails before you can push, see Fix: Docker Build Failed for build-specific troubleshooting.

Still Not Working?

”Manifest unknown” but the image definitely exists

Sometimes Docker’s local state is out of sync with the registry. Try clearing the local cache:

# Remove the local copy of the image
docker rmi myapp:v1.0

# Pull again
docker pull myapp:v1.0

If you use Docker Desktop, try restarting the Docker engine from the system tray icon or running:

# On macOS / Linux
sudo systemctl restart docker

# On Windows (PowerShell)
Restart-Service docker

Error only happens in CI but works locally

This is almost always an authentication or rate-limit issue. CI runners share IP addresses, making rate limits hit faster. And CI environments often do not have the same credentials as your local machine.

Add explicit login steps to your CI pipeline:

# GitHub Actions example
- name: Log in to Docker Hub
  uses: docker/login-action@v3
  with:
    username: ${{ secrets.DOCKERHUB_USERNAME }}
    password: ${{ secrets.DOCKERHUB_TOKEN }}

Pull works but docker run says “image not found”

If docker pull succeeds but docker run cannot find the image, you are probably referencing a different tag or name:

# You pulled this
docker pull mycompany/myapp:v1.0

# But you are running this (different tag)
docker run mycompany/myapp:v1

Check your local images:

docker images | grep myapp

Make sure the exact name and tag match between your pull and run commands.

DNS resolution failures masquerading as “not found”

If Docker cannot resolve the registry hostname, the error can look like the image does not exist:

# Test DNS resolution
nslookup registry-1.docker.io
dig registry-1.docker.io

If DNS fails, check your /etc/resolv.conf or Docker’s DNS configuration:

{
  "dns": ["8.8.8.8", "8.8.4.4"]
}

Add this to /etc/docker/daemon.json and restart Docker.

Private registry with self-signed certificates

If your private registry uses a self-signed TLS certificate, Docker will refuse to connect and may report the image as not found instead of showing a certificate error.

Add the registry as an insecure registry (development only):

{
  "insecure-registries": ["registry.example.com:5000"]
}

Or install the CA certificate on the Docker host:

sudo cp my-ca.crt /etc/docker/certs.d/registry.example.com:5000/ca.crt
sudo systemctl restart docker

This is the correct approach for production environments. Never use insecure-registries in production.


Related: If your container starts but cannot reach other services, see Fix: Docker Container Not Connecting to Network or Other Containers.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles