Skip to content

Fix: AWS ECR Authentication Failed (docker login and push Errors)

FixDevs · (Updated: )

Part of:  Docker, DevOps & Infrastructure

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 credentials

Or after a successful login that has since expired:

Error response from daemon: failed to authorize: failed to fetch anonymous token:
unexpected status code 401 Unauthorized

Or in CI/CD:

Error: Cannot perform an interactive login from a non TTY device

Or 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-app

Why This Happens

ECR authentication works differently from Docker Hub. Instead of a static username/password, ECR uses temporary tokens issued by AWS. aws ecr get-login-password is a thin wrapper around the GetAuthorizationToken API call — it returns a base64-encoded credential blob that Docker treats as an opaque password. That blob has a hard 12-hour TTL, and there is no refresh; you have to call the API again.

A second source of failures is how the AWS SDK resolves credentials before it even calls ECR. The credential chain walks through environment variables, then ~/.aws/credentials, then ~/.aws/config, then the EC2 instance metadata service (IMDS), then the ECS container metadata endpoint, then the IRSA service account token. If any earlier step returns a valid-but-wrong credential — like a leftover personal IAM user with no ECR access — the chain stops there and you get permission errors instead of the role you expected.

Common root causes:

  • ECR tokens expire after 12 hoursdocker login to 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, or ecr:PutImage permissions.
  • AWS credentials not configuredaws ecr get-login-password requires AWS credentials in the environment.
  • Wrong credential picked from the chain — the SDK silently fell back to a different profile, leftover env var, or expired SSO session.
  • 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.
  • IMDSv2 blocked — when running on EC2, IMDSv2 requires session tokens. Containers without --http-tokens required configured properly cannot reach the metadata endpoint.
  • VPC endpoint policy denies the action — private subnets routing ECR traffic through a VPC endpoint can be silently blocked by an over-restrictive endpoint 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.com

What each part does:

  • aws ecr get-login-password — calls AWS to get a temporary password (valid 12 hours)
  • --username AWS — the username is always literally AWS for 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 table

Then 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:latest

Note: The old aws ecr get-login command (which returned a full docker login command) is deprecated. Always use get-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 pushing

Create 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:GetAuthorizationToken must 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:latest

If 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-1

Pro 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 permissions

Fix 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 up

Use 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.

In Production: Incident Lens

ECR auth failures are deploy-time incidents. The blast radius depends on what is consuming the image:

  • Kubernetes — new pods fail with ImagePullBackOff or ErrImagePull. Horizontal Pod Autoscaler scale-up events fail. If you are mid-rolling-deploy, the new ReplicaSet stalls while the old one continues serving. If you are mid-autoscale during a traffic spike, you cannot add capacity and existing pods saturate.
  • ECS / Fargate — tasks fail to start with CannotPullContainerError. Service desired count drops below running count. The service event log fills with pull failures.
  • Lambda container images — function invocations fail with Container image is not yet ready or auth errors. Cold starts that should take seconds time out.
  • EC2 user-data bootstrap — instances launch but never run the app. Auto Scaling Group health checks fail and the ASG cycles instances forever.

Detection signals to wire up:

  • CloudWatch metric: ImagePullBackOff events from container insights (Kubernetes) — alert at count > 0 sustained 5 minutes
  • ECS event log filter for CannotPullContainerError
  • CloudTrail filter on GetAuthorizationToken errors — spike indicates rotation or revocation issues
  • Lambda function error metric segmented by error type
  • Synthetic canary that pulls a known-good image every 5 minutes from each region

Recovery playbook:

  1. Confirm the failure is auth, not network or quotaaws ecr describe-images --repository-name <name> from the same network as the failing workload. If this works, auth is the issue. If it hangs, it is networking (VPC endpoint, security group, NAT).
  2. Identify whose credentials are failing — for Kubernetes, check kubectl describe pod for the exact error and the service account. For ECS, check the task execution role. For Lambda, check the function role.
  3. Rotate or reissue credentials — for long-lived access keys, generate new keys in IAM and update the secret. For IRSA, verify the OIDC provider thumbprint is current. For instance profiles, verify the role is still attached.
  4. Re-trigger the pullkubectl rollout restart deployment/<name> or force-update the ECS service. For Lambda, redeploy the function.
  5. Verify the registry endpoint is reachablenc -zv <account>.dkr.ecr.<region>.amazonaws.com 443 from a debug pod inside the same network.

Prevention:

  • Use short-lived OIDC tokens (GitHub Actions OIDC, GitLab JWT, CircleCI OIDC) to assume IAM roles instead of long-lived access keys. No secret to leak, no rotation to schedule, no 12-hour token to refresh.
  • For workloads on EKS, use IRSA (IAM Roles for Service Accounts) — pod-level identity scoped to exactly the ECR repos that pod needs.
  • For Fargate or EC2, attach least-privilege IAM roles directly — never embed keys in task definitions or user-data.
  • Enforce IMDSv2 with --http-tokens required and --http-put-response-hop-limit 1 to prevent SSRF leaks of instance credentials.
  • Add a startup probe that does not need ECR (a tiny init container that exits 0) before the main container, so you can distinguish image-pull failures from app failures.
  • Cache critical base images in a regional ECR pull-through cache so a single region’s ECR outage does not freeze deploys.

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 up

Check 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 repos

Check 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/permissions

Check for clock skew on the host. AWS signature verification rejects requests whose timestamp is more than 5 minutes off from AWS server time. Containers with a broken time source — or hosts that drift after long uptime — see signature errors that look like auth failures:

date -u
# Compare against an authoritative source
curl -sI https://aws.amazon.com | grep -i ^date

Check for an inline session-token mismatch. If you use STS temporary credentials, the access key, secret key, and session token must all come from the same assume-role response. Mixing the access key from one session with the session token from another produces InvalidClientTokenId.

Check for ECR throttling. During mass scale-outs, ECR can throttle GetAuthorizationToken and BatchGetImage. Errors look like 400-class auth failures but the CloudTrail entry shows ThrottlingException. Use a pull-through cache or stagger pod startup to reduce concurrent token requests.

Check the platform architecture. A pod scheduled on an ARM node trying to pull an x86-only image returns a manifest error that can surface as a confusing auth-adjacent failure. Use multi-arch manifests (docker buildx) and verify with docker manifest inspect.

For related AWS and Docker issues, see Fix: AWS IAM AccessDeniedException, Fix: Docker Permission Denied on Socket, Fix: AWS ECS Task Failed to Start, and Fix: AWS Unable to Locate Credentials.

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