Fix: AWS ECR Authentication Failed (docker login and push Errors)
Quick Answer
How to fix AWS ECR authentication errors — no basic auth credentials, token expired, permission denied on push, and how to authenticate correctly from CI/CD pipelines and local development.
The Error
Pushing to Amazon ECR fails with:
Error response from daemon: no basic auth credentialsOr after a successful login that has since expired:
Error response from daemon: failed to authorize: failed to fetch anonymous token:
unexpected status code 401 UnauthorizedOr in CI/CD:
Error: Cannot perform an interactive login from a non TTY deviceOr with permission errors:
Error response from daemon: denied: User: arn:aws:iam::123456789:user/deploy
is not authorized to perform: ecr:InitiateLayerUpload on resource:
arn:aws:ecr:us-east-1:123456789:repository/my-appWhy This Happens
ECR authentication works differently from Docker Hub. Instead of a static username/password, ECR uses temporary tokens issued by AWS:
- ECR tokens expire after 12 hours —
docker loginto ECR is valid for 12 hours. After that, all pushes and pulls fail with auth errors. - Wrong region in the registry URL — the ECR URL is region-specific (
123456789.dkr.ecr.us-east-1.amazonaws.com). Using the wrong region causes auth failures. - Missing IAM permissions — the AWS user or role doesn’t have
ecr:GetAuthorizationToken,ecr:InitiateLayerUpload,ecr:UploadLayerPart,ecr:CompleteLayerUpload, orecr:PutImagepermissions. - AWS credentials not configured —
aws ecr get-login-passwordrequires AWS credentials in the environment. - Non-existent repository — pushing to a repository that hasn’t been created yet.
- Cross-account push — pushing from one AWS account to an ECR in another requires explicit resource-based policy.
Fix 1: Authenticate Correctly with get-login-password
The correct way to authenticate to ECR:
# Authenticate Docker to ECR (replace region and account ID)
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.comWhat each part does:
aws ecr get-login-password— calls AWS to get a temporary password (valid 12 hours)--username AWS— the username is always literallyAWSfor ECR--password-stdin— reads the password from stdin (avoids it appearing in shell history)- The registry URL is your account ID + region +
amazonaws.com
Find your account ID and registry URL:
# Get your AWS account ID
aws sts get-caller-identity --query Account --output text
# List your ECR repositories and their URIs
aws ecr describe-repositories --region us-east-1 \
--query 'repositories[*].repositoryUri' --output tableThen push:
# Tag your image for ECR
docker tag my-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
# Push
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latestNote: The old
aws ecr get-logincommand (which returned a fulldocker logincommand) is deprecated. Always useget-login-password | docker login --password-stdin.
Fix 2: Create the ECR Repository If It Doesn’t Exist
ECR does not auto-create repositories on push. If the repository doesn’t exist, the push fails:
# Create the repository
aws ecr create-repository \
--repository-name my-app \
--region us-east-1 \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES256
# Output includes the repositoryUri — use this for tagging and pushingCreate with a lifecycle policy to limit image retention:
# Create the repository
aws ecr create-repository --repository-name my-app --region us-east-1
# Apply lifecycle policy (keeps only the last 10 images)
aws ecr put-lifecycle-policy \
--repository-name my-app \
--region us-east-1 \
--lifecycle-policy-text '{
"rules": [{
"rulePriority": 1,
"description": "Keep last 10 images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 10
},
"action": { "type": "expire" }
}]
}'Fix 3: Fix IAM Permissions
The AWS identity used to push needs specific ECR permissions:
Minimum IAM policy for pushing to a specific repository:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRAuthentication",
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Sid": "ECRPush",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": "arn:aws:ecr:us-east-1:123456789012:repository/my-app"
}
]
}Note:
ecr:GetAuthorizationTokenmust have"Resource": "*"— it’s a global action that cannot be scoped to a specific repository.
Check what permissions the current identity has:
aws iam get-user
aws iam list-attached-user-policies --user-name deploy-user
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/deploy-user \
--action-names ecr:GetAuthorizationToken ecr:PutImage \
--resource-arns "*"Fix 4: Authenticate in GitHub Actions
The ECR token expires after 12 hours — regenerate it in every CI run:
# .github/workflows/deploy.yml
name: Build and Push to ECR
on:
push:
branches: [main]
env:
AWS_REGION: us-east-1
ECR_REPOSITORY: my-app
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC authentication
contents: read
steps:
- uses: actions/checkout@v4
# Configure AWS credentials via OIDC (no long-lived access keys)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsECRRole
aws-region: ${{ env.AWS_REGION }}
# Login to ECR — generates a fresh 12-hour token
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push image to ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latestIf using long-lived access keys instead of OIDC:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1Pro Tip: Use OIDC (OpenID Connect) instead of long-lived access keys. OIDC generates short-lived credentials for each CI run — no secrets to rotate or leak. Set up the GitHub OIDC provider in IAM and create a role that trusts it.
Fix 5: Authenticate from ECS Tasks, Lambda, or EC2
When running on AWS compute, use the instance/task IAM role instead of hardcoded credentials:
ECS Task Role — attach to your task definition:
{
"taskDefinition": {
"taskRoleArn": "arn:aws:iam::123456789012:role/MyECSTaskRole",
"executionRoleArn": "arn:aws:iam::123456789012:role/MyECSExecutionRole"
}
}The execution role (not the task role) needs ECR pull permissions — ECS uses it to pull the image before starting the task:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "*"
}]
}EC2 instance — authenticate using the instance profile:
# On the EC2 instance — uses the instance profile automatically
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.com
# The instance profile role needs ECR permissionsFix 6: Pull Images from ECR in Docker Compose
For local development pulling from ECR:
# docker-compose.yml
services:
app:
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
ports:
- "3000:3000"# Authenticate first, then pull
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.com
docker compose pull
docker compose upUse the Amazon ECR Docker Credential Helper to avoid manual login:
# Install the credential helper
go install github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login@latest
# or via package manager
brew install docker-credential-helper-ecr
# Configure Docker to use it
cat ~/.docker/config.json// ~/.docker/config.json
{
"credHelpers": {
"123456789012.dkr.ecr.us-east-1.amazonaws.com": "ecr-login",
"public.ecr.aws": "ecr-login"
}
}With the credential helper, Docker automatically gets fresh ECR tokens when needed — no manual docker login required.
Still Not Working?
Verify your AWS credentials are configured:
aws sts get-caller-identity
# Returns your account ID, user ID, and ARN
# If this fails, your credentials are not set upCheck the ECR endpoint for your region:
# ECR URLs are region-specific
# Format: <account-id>.dkr.ecr.<region>.amazonaws.com
# Make sure region matches your repository's region
aws ecr describe-repositories --region us-east-1
# vs
aws ecr describe-repositories --region ap-northeast-1 # Different region, different reposCheck for VPC endpoint issues. If your EC2 or ECS runs in a private subnet and uses an ECR VPC endpoint, ensure the endpoint policy allows the required actions.
Test pulling a public ECR image to isolate the issue:
# Pull a public image from ECR Public Gallery (no auth needed)
docker pull public.ecr.aws/amazonlinux/amazonlinux:latest
# If this works but your private ECR fails, the issue is auth/permissionsFor related AWS and Docker issues, see Fix: AWS IAM AccessDeniedException and Fix: Docker Permission Denied on Socket.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: AWS ECS Task Failed to Start
How to fix ECS tasks that fail to start — port binding errors, missing IAM permissions, Secrets Manager access, essential container exit codes, and health check failures.
Fix: Docker Multi-Stage Build COPY --from Failed
How to fix Docker multi-stage build errors — COPY --from stage not found, wrong stage name, artifacts not at expected path, and BuildKit caching issues.
Fix: GitHub Actions Environment Variables Not Available Between Steps
How to fix GitHub Actions env vars and outputs not persisting between steps — GITHUB_ENV, GITHUB_OUTPUT, job outputs, and why echo >> $GITHUB_ENV is required.
Fix: AWS CloudWatch Logs Not Appearing
How to fix AWS CloudWatch logs not showing up — IAM permissions missing, log group not created, log stream issues, CloudWatch agent misconfiguration, and Lambda log delivery delays.