You can push container images to Amazon ECR from GitHub Actions, but the most surprising thing is how little the actual image push matters compared to the setup that enables it.
Let’s see this in action. Imagine you have a Dockerfile, and you want to build it and push it to ECR every time you merge to main. Here’s a snippet of a GitHub Actions workflow:
name: Build and Push Docker Image
on:
push:
branches:
- main
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build and push Docker image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: my-app
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
This workflow does a few key things: checks out your code, sets up a build environment, authenticates to AWS, logs into ECR, and then builds and pushes the image. The docker push command itself is pretty standard, but the magic is in how we got there.
The problem this solves is getting your application’s containerized build artifacts reliably into a central, secure registry that your cloud environment can access. Without this, you’d be manually building images, uploading them to S3, and then pulling them down to your EC2 instances or ECS tasks, which is a recipe for inconsistency and errors. GitHub Actions automates this, integrating it directly into your CI/CD pipeline.
Internally, the aws-actions/configure-aws-credentials action uses the provided AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to create temporary credentials or use them directly to set up the AWS CLI environment within the GitHub Actions runner. The aws-actions/amazon-ecr-login action then leverages these configured credentials to obtain a pre-signed Docker login command specific to your ECR repository. This command is used by the Docker client on the runner to authenticate with ECR, allowing subsequent docker push operations to succeed.
The ECR_REGISTRY output from the amazon-ecr-login action is crucial. It’s not just a generic AWS endpoint; it’s the specific registry URL for your ECR repository, formatted like 123456789012.dkr.ecr.us-east-1.amazonaws.com. This ensures your Docker client targets the correct location. When you build the image, you tag it with this registry URL, the repository name (which you define, e.g., my-app), and a unique tag (often the Git commit SHA for traceability). The docker push command then uses this fully qualified image name to send the image layers to your ECR.
What most people overlook is the interplay between the IAM permissions you grant your AWS credentials and the ECR repository policy. The GitHub Actions runner’s AWS credentials must have ecr:GetAuthorizationToken, ecr:BatchCheckLayerAvailability, ecr:InitiateLayerUpload, ecr:UploadLayerPart, ecr:CompleteLayerUpload, and ecr:PutImage permissions for the specific ECR repository. If any of these are missing, the docker push will fail, often with cryptic authentication or authorization errors that don’t immediately point to IAM.
The next hurdle is managing different image tags effectively, especially when dealing with multiple branches or release versions.