Skip to content

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

FixDevs ·

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:

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

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

For related AWS and Docker issues, see Fix: AWS IAM AccessDeniedException and Fix: Docker Permission Denied on Socket.

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