1. Introduction
AWS Elastic Container Registry (ECR) doesn't use static credentials like Docker Hub. It uses short-lived tokens generated by the AWS CLI, which expire every 12 hours. This design is more secure — but it also means authentication failures happen more often and for more varied reasons.
This guide covers every common ECR login failure: expired tokens, missing IAM permissions, misconfigured credential helpers, cross-account access problems, and failures specific to CI/CD pipelines and Kubernetes. For each one you'll get the exact error message to look for, a diagnosis command, and the fix.
2. How ECR Authentication Works
Before debugging, it helps to understand the authentication flow. ECR does not accept a static username and password. Instead:
- You call the AWS API (ecr:GetAuthorizationToken) to request a temporary token
- AWS returns a base64-encoded token that is valid for 12 hours
- You pass that token to docker login as the password, with the username AWS
- Docker stores the credential in ~/.docker/config.json
- Every docker pull or docker push to ECR uses that stored credential until it expires
This means authentication failures have two broad categories: problems getting the token (IAM, credentials, region) and problems using the token (expired, wrong registry URL, missing credential helper).
# The standard ECR login command aws ecr get-login-password --region <region> \ | docker login --username AWS --password-stdin \ <account-id>.dkr.ecr.<region>.amazonaws.com
# Example for us-east-1: aws ecr get-login-password --region us-east-1 \ | docker login --username AWS --password-stdin \ 123456789012.dkr.ecr.us-east-1.amazonaws.com
3. Common Causes
- ECR token has expired (tokens are valid for 12 hours only)
- The IAM user or role lacks ecr:GetAuthorizationToken permission
- The IAM entity lacks ecr:BatchGetImage, ecr:GetDownloadUrlForLayer for pull, or ecr:InitiateLayerUpload for push
- Wrong AWS region in the login command vs the registry URL
- Wrong AWS account ID in the registry URL
- No AWS credentials configured in the environment (missing ~/.aws/credentials or env vars)
- The ECR credential helper is not installed or not configured for the registry domain
- Cross-account access: the ECR repository policy does not grant access to the calling account/role
- EC2 instance profile or ECS task role does not have the necessary ECR permissions
- Kubernetes imagePullSecret contains an expired ECR token
4. Step-by-Step Diagnosis and Fix
Step 1: Read the exact error message
ECR authentication failures produce different errors depending on where the problem is. Before doing anything, capture the exact message:
# Run with verbose output to see the full error aws ecr get-login-password --region us-east-1 2>&1
# Then try the full login command and capture output aws ecr get-login-password --region us-east-1 \ | docker login --username AWS --password-stdin \ 123456789012.dkr.ecr.us-east-1.amazonaws.com 2>&1
Common error signatures and what they indicate:
| Error message / signal | Likely cause |
|---|---|
| An error occurred (UnauthorizedException) | IAM lacks ecr:GetAuthorizationToken — fix: add the permission to the IAM policy |
| Error: Cannot perform an interactive login | Piping issue or docker credential helper conflict — fix: check credential helper config |
| unauthorized: authentication required | Token expired or not passed correctly — fix: re-run get-login-password and docker login |
| no basic auth credentials | docker login was never run for this registry — fix: run the full ECR login command |
| denied: User is not authorized to perform: ecr:* | Missing ECR repository-level IAM permission — fix: add policy for the specific actions |
| no such host / could not resolve | Wrong region or account ID in registry URL — fix: verify the registry URI format |
| RequestExpired / Token has expired | System clock skew or using an old token — fix: sync clock, re-generate token |
Step 2: Verify AWS credentials are present and correct
The most fundamental check — confirm that valid AWS credentials exist in the environment where the login command runs:
# Check which credentials are active aws sts get-caller-identity
# Expected output (IAM user):
# {
# "UserId": "AIDAEXAMPLEID",
# "Account": "123456789012",
# "Arn": "arn:aws:iam::123456789012:user/ci-deploy"
# }
# Expected output (IAM role / instance profile):
# {
# "UserId": "AROAEXAMPLEID:session-name",
# "Account": "123456789012",
# "Arn": "arn:aws:sts::123456789012:assumed-role/my-role/session-name"
# }
# If this fails, your credentials are missing or invalid.
# Check environment variables: echo $AWS_ACCESS_KEY_ID echo $AWS_PROFILE cat ~/.aws/credentials
Step 3: Check the IAM permissions
Getting an ECR token and pulling or pushing images requires specific IAM actions. Check what the active identity is allowed to do:
# Simulate the ecr:GetAuthorizationToken permission aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:user/ci-deploy \ --action-names ecr:GetAuthorizationToken \ --resource-arns '*'
# Check effective permissions for ECR actions on a specific repo aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:user/ci-deploy \ --action-names ecr:BatchGetImage ecr:GetDownloadUrlForLayer \ ecr:InitiateLayerUpload ecr:UploadLayerPart \ ecr:CompleteLayerUpload ecr:PutImage \ --resource-arns arn:aws:ecr:us-east-1:123456789012:repository/myapp
Minimum IAM policy for ECR pull:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ecr:GetAuthorizationToken", "Resource": "*" }, { "Effect": "Allow", "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage" ], "Resource": "arn:aws:ecr:<region>:<account-id>:repository/<repo-name>" } ] }
For push access, add these actions to the resource-scoped statement:
Step 4: Fix an expired token
If you authenticated successfully before but now get 'no basic auth credentials' or '401 Unauthorized', your 12-hour token has expired. The fix is simply to re-run the login command:
# Re-authenticate to ECR aws ecr get-login-password --region <region> \ | docker login --username AWS --password-stdin \ <account-id>.dkr.ecr.<region>.amazonaws.com
# Confirm the new credential is stored cat ~/.docker/config.json | grep dkr.ecr
In CI/CD pipelines, this should happen at the start of every pipeline run, not once during setup. A token from a previous pipeline run will be stale by the time the next run executes — especially for scheduled or infrequent pipelines.
Step 5: Verify the registry URL format
A wrong account ID or region in the registry URL produces misleading errors. Double-check the exact format:
# Correct private ECR registry URL format:
# <12-digit-account-id>.dkr.ecr.<region>.amazonaws.com
# Verify your account ID aws sts get-caller-identity --query Account --output text
# List your ECR repositories with their full URIs aws ecr describe-repositories --region <region> \ --query 'repositories[*].repositoryUri' --output table
# Common mistakes:
# Wrong: 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp (missing region match)
# Wrong: dkr.ecr.us-east-1.amazonaws.com/myapp (missing account ID)
# Correct: 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp
Step 6: Fix cross-account ECR access
If you're pulling an image from an ECR repository in a different AWS account, the repository needs a resource-based policy that explicitly grants access to the calling account or role:
# Check the current ECR repository policy aws ecr get-repository-policy \ --repository-name myapp \ --region us-east-1
# Add a policy that grants cross-account pull access aws ecr set-repository-policy \ --repository-name myapp \ --region us-east-1 \ --policy-text '{ "Version": "2012-10-17", "Statement": [ { "Sid": "CrossAccountPull", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::<calling-account-id>:root" }, "Action": [ "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:BatchCheckLayerAvailability" ] } ] }'
For cross-account access, the calling identity still needs ecr:GetAuthorizationToken in their own account — you cannot delegate that via a resource policy. The resource policy only governs the repository-level actions (pull/push).
Step 7: Fix the ECR credential helper
The Amazon ECR Credential Helper (docker-credential-ecr-login) automates token refresh so you never have to run docker login manually. If it's misconfigured, you may see 'no basic auth credentials' even when your IAM permissions are correct:
# Install the credential helper (Linux) sudo apt-get install amazon-ecr-credential-helper
# Or: go install github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login@latest
# Configure Docker to use it for all ECR registries
# Add to ~/.docker/config.json: { "credHelpers": { "<account-id>.dkr.ecr.<region>.amazonaws.com": "ecr-login" } }
# Or configure it as the default helper for all ECR domains: { "credsStore": "ecr-login" }
# Test the helper directly docker-credential-ecr-login get \ <<< '<account-id>.dkr.ecr.<region>.amazonaws.com'
5. Verification Steps
Once you've applied your fix, run these checks to confirm authentication is working end-to-end:
# 1. Confirm AWS identity aws sts get-caller-identity
# 2. Get a fresh ECR token aws ecr get-login-password --region <region>
# Should output a long base64 token string, no errors
# 3. Run the full login aws ecr get-login-password --region <region> \ | docker login --username AWS --password-stdin \ <account-id>.dkr.ecr.<region>.amazonaws.com
# Expected: Login Succeeded
# 4. Pull a known image from ECR to confirm end-to-end docker pull <account-id>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>
# 5. For push access, verify by pushing a test tag docker tag alpine:latest \ <account-id>.dkr.ecr.<region>.amazonaws.com/<repo>:test docker push <account-id>.dkr.ecr.<region>.amazonaws.com/<repo>:test
6. Common Mistakes
- Scoping ecr:GetAuthorizationToken to a specific repository ARN instead of '*' — this silently fails
- Running docker login once during initial setup and expecting it to work indefinitely — tokens expire after 12 hours
- Using the wrong region in the registry URL — the login region and the registry URL region must match exactly
- Configuring credsStore: "ecr-login" globally and breaking Docker Hub or other registry logins
- Forgetting that cross-account access requires both an IAM policy on the caller AND a repository resource policy on the target account
- Using aws ecr get-login (deprecated) instead of aws ecr get-login-password — the old command is removed in newer AWS CLI versions
- Assuming an EC2 instance profile has ECR permissions without verifying — instance profiles need explicit ECR policies attached
- Not accounting for token expiry in Kubernetes imagePullSecrets — static ECR secrets will expire overnight
7. Prevention Tips
- Always run the ECR login command at the start of every CI/CD pipeline run — never rely on a cached token from a previous run
- Use the ECR Credential Helper on developer machines and build servers to handle token refresh automatically
- For EKS workloads, use IRSA to give pod service accounts direct ECR access — eliminates the imagePullSecret expiry problem entirely
- Tag your ECR IAM policies with a description noting which services depend on them — reduces the chance of accidental permission removal
- Create a dedicated IAM role or user for ECR push with minimal permissions (push only to specific repos), separate from the pull role used by runtime workloads
- Set up CloudWatch alarms or AWS Config rules to alert on IAM policy changes that affect ECR access
- Document your ECR registry URIs and the IAM roles that use them — this speeds up debugging significantly when auth fails at 2am
8. Summary
ECR authentication failures are almost always one of four things: missing IAM permissions, an expired token, a wrong registry URL, or a misconfigured credential helper. Here's the quick-reference diagnostic:
| Symptom | Most likely cause and fix |
|---|---|
| UnauthorizedException on get-login-password | IAM lacks ecr:GetAuthorizationToken — add it with Resource: "*" |
| no basic auth credentials | Token expired or login never run — re-run the full ECR login command |
| denied: not authorized to perform ecr:* | Missing repo-level IAM permissions — add BatchGetImage and related actions |
| no such host / DNS failure | Wrong region or account ID in registry URL — verify with ecr describe-repositories |
| cross-account 403 | Missing ECR repository resource policy — add cross-account principal |
| works locally, fails in CI | CI environment missing AWS credentials — check env vars and IAM role binding |
| works today, fails tomorrow | 12-hour token expiry — add login step to pipeline; use credential helper or IRSA |
For any environment running long-lived workloads — Kubernetes clusters, persistent CI agents, or always-on build servers — the right long-term fix is to eliminate static ECR tokens entirely by using the credential helper or IRSA. When ECR auth fails in a running Kubernetes cluster, it surfaces as ImagePullBackOff. The 12-hour expiry will catch you eventually if you don't.