Pull Images from Private Registries
Learn how to pull Docker images from private registries with authentication, hands-on steps, and common troubleshooting tips.
Focus: pull images from private registries
You just built a Docker image and pushed it to a registry — but when you try docker pull my-private-image from another machine, you get a dreaded denied: requested access to the resource is denied error. Public registries are open to anyone, but real-world projects store sensitive code, proprietary binaries, or paid dependencies in private registries. Without authentication, Docker has no idea who you are and refuses to serve the image. This lesson teaches you how to pull images from private registries securely and reliably.
The problem this lesson solves
When your Docker images live in a private registry (like Docker Hub private repos, Amazon ECR, or a self-hosted Harbor instance), standard docker pull fails because the registry requires proof of identity. The error is usually:
Error response from daemon: pull access denied for mycorp/app, repository does not exist or may require 'docker login'
This blocks development, CI/CD pipelines, and deployments. You need a repeatable, automation-friendly way to authenticate and pull private images without exposing credentials in your Dockerfile or shell history.
Core concept / mental model
Think of a private registry like a gated warehouse: you can't just walk in and take a box — you need a badge that proves you have permission. Docker uses authentication tokens as that badge.
- Registry — a server that stores Docker images (e.g.,
registry.example.com). - Repository — a named collection of images (e.g.,
mycorp/app). - Credentials — a username/password or a short-lived token.
- docker login — the command that exchanges your credentials for an authentication token and caches it locally in
~/.docker/config.json. - docker pull — once authenticated, Docker includes the token in the request; the registry verifies it and streams the image layers.
Pro tip: Never store plaintext passwords in CI scripts. Use access tokens (e.g., Docker Hub personal access tokens) that you can revoke individually.
How it works step by step
-
Choose your authentication method. Most registries support: - Username + password (basic auth) — okay for interactive use. - Personal access token (PAT) — recommended for automation, revocable. - AWS IAM / cloud provider roles — for ECR, GCR, etc.
-
Run
docker loginagainst the registry URL. -
Docker stores the token in
~/.docker/config.json(base64-encoded, not encrypted). -
Run
docker pull— Docker automatically attaches the stored token. -
Optional: use
docker logoutto clear credentials when done.
For automation (CI/CD, Kubernetes), you can pass credentials via:
- stdin (secure): cat password.txt | docker login --username myuser --password-stdin
- Environment variables: DOCKER_USER, DOCKER_PASS (then printed to stdin)
A configuration file (avoid if possible — it's a file with secrets).
Hands-on walkthrough
Example 1: Pulling from a private Docker Hub repository
Assume you have a private repo on Docker Hub called mycompany/private-app.
# Step 1: Create a personal access token on hub.docker.com -> Account Settings -> Security
# Step 2: Log in with that token (not your account password)
docker login --username mycompanyuser --password-stdin <<< "dckr_pat_abc123def456"
# Step 3: Pull the private image
docker pull mycompany/private-app:latest
Expected output:
latest: Pulling from mycompany/private-app
Digest: sha256:abc...
Status: Downloaded newer image for mycompany/private-app:latest
Example 2: Pulling from a self-hosted registry with custom port
Many teams run their own registry (e.g., with Harbor or Docker Registry) at a custom port like 5000.
# Login
docker login registry.company.com:5000 --username deploy --password-stdin <<< "supersecret"
# Pull the image
docker pull registry.company.com:5000/team-app:1.2.3
Expected output:
1.2.3: Pulling from registry.company.com:5000/team-app
Digest: sha256:def...
Status: Image is up to date for registry.company.com:5000/team-app:1.2.3
Example 3: Using AWS ECR (IAM-based auth)
AWS ECR doesn't use docker login directly — you must run aws ecr get-login-password.
# Requires AWS CLI and IAM role with ECR permissions
aws ecr get-login-password --region us-west-2 | \
docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-west-2.amazonaws.com
# Pull
docker pull 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
Pro tip: In CI, use the
aws-actions/configure-aws-credentialsaction to generate IAM credentials first.
Example 4: Non-interactive login for scripts (stdin)
Never echo passwords in bash -c — use stdin redirection.
#!/bin/bash
# Safe way to pass password from a file or env var
docker login --username "$DOCKER_USER" --password-stdin < "${DOCKER_PASS_FILE}"
Or write an inline heredoc:
docker login --username robot --password-stdin << EOF
dckr_pat_abc...
EOF
Compare options / when to choose what
| Method | Use case | Security | Automation-friendly |
|---|---|---|---|
| Username + password (interactive) | Quick dev test, single person | Low — password cached in plaintext on disk | No — requires manual typing |
| Personal access token (PAT) | CI/CD, multiple developers | High — revocable, scoped | Yes — pass via stdin |
| AWS ECR IAM | Cloud-native, AWS-only | High — temporary tokens | Yes — via get-login-password |
| OIDC / credential helpers | Kubernetes, enterprise | Very high — no static secrets | Yes — fully automated |
Troubleshooting & edge cases
Common mistakes and fixes
denied: requested access to the resource is denied— you didn't login, or credentials are wrong. Rundocker loginagain with correct token.Error: cannot ...— the registry URL is incorrect or the image doesn't exist. Double-check the full path including port and repository name.http: server gave HTTP response to HTTPS client— you're connecting to an HTTP-only registry. For local/dev registries, add it to Docker's insecure registries list (/etc/docker/daemon.json):{"insecure-registries":["myregistry.local:5000"]}.no basic auth credentials— even after login, Docker may not find the token because you logged into a different registry URL. Ensure the server indocker loginmatches the one indocker pull.- Token expiry — ECR tokens expire after 12 hours. In long-running CI pipelines, re-authenticate before pulling.
Edge cases
- Multi-tenant registries (e.g., GitHub Container Registry): you must specify the full image name including namespace.
- Proxied environments: set
HTTP_PROXYandHTTPS_PROXYbeforedocker login. - Windows: use
$ | docker loginwith PowerShell instead of pipe.
What you learned & what's next
You now understand:
- Why private registries require authentication (
pull images from private registriesstarts withdocker login). - How to authenticate interactively and non-interactively using personal access tokens or cloud IAM.
- How to pull images from Docker Hub private repos, self-hosted registries, and AWS ECR.
- Common pitfalls like wrong URLs, HTTP-only registries, and token expiry.
What's next: Now that you can pull private images, the next lesson covers pushing images to private registries — including tagging, versioning, and CI/CD integration. You'll apply the same authentication patterns to push (write) instead of pull (read).
Pro tip: Practice by setting up a free Docker Hub account, creating a personal access token, and pulling a private image you built earlier.
Practice recap
Set up a free Docker Hub account, create a personal access token with read scope, and pull a private image you previously pushed (or create a new one with docker tag busybox myuser/private-box && docker push). Then test pulling it from a different machine or terminal session to confirm the workflow works end-to-end.
Common mistakes
- Using your Docker Hub account password instead of a personal access token — tokens are revocable and scoped to specific repos.
- Forgetting to include the registry hostname/port in
docker pull— a self-hosted registry on port 5000 requiresregistry.example.com:5000/repo:tag. - Hardcoding credentials in shell scripts with
echo password | docker login— always use--password-stdinwith<or<<<redirection to avoid exposing secrets in process lists. - Assuming
docker loginpersists across user sessions in CI environments — each CI job needs its own login step, and token expiration (especially for ECR) requires re-authentication.
Variations
- Use a credential helper (e.g.,
docker-credential-ecr-login) that automatically refreshes short-lived tokens and integrates with cloud IAM. - For Kubernetes, create a
docker-registrysecret and reference it in Pod specs — no manualdocker loginneeded on nodes. - Some registries support OIDC-based authentication (e.g., GitLab Container Registry with JWT tokens) — no static secrets exist on disk.
Real-world use cases
- CI/CD pipeline pulls a private Docker image for unit testing, ensuring no proprietary code leaks to a public registry.
- Production Kubernetes cluster pulls microservice images from a private Harbor registry behind a corporate firewall.
- Developer pulls a teammate's private build artifact from a shared Docker Hub private repo for debugging integration issues.
Key takeaways
- To pull images from private registries, you must first authenticate with
docker login— never skip it. - Use personal access tokens instead of account passwords — they are revocable and scoped.
- For cloud registries like AWS ECR, use IAM-based authentication via
aws ecr get-login-password. - When automating, pass credentials via stdin (
--password-stdin) to avoid secrets in logs or process lists. - Always confirm the full registry URL (including port) matches between
docker loginanddocker pull. - Token expiry is common — re-authenticate in long-running jobs, especially with ECR (12-hour limit).
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.