Build a Docker CI/CD Pipeline with GitHub Actions and AWS ECR (2026)
Step-by-step guide to building a production CI/CD pipeline that builds, scans, and pushes Docker images to AWS ECR using GitHub Actions.
This is the most common DevOps task you'll build in your first job: a pipeline that automatically builds your Docker image, scans it for vulnerabilities, and pushes it to ECR every time code is merged.
Here's the complete setup, step by step.
What You'll Build
Push to main branch
↓
GitHub Actions triggers
↓
1. Checkout code
2. Run unit tests
3. Build Docker image (tagged with git SHA)
4. Scan image for vulnerabilities (Trivy)
5. Authenticate to AWS ECR
6. Push image to ECR
7. Slack notification ✅
Prerequisites
- GitHub repository with your app
- AWS account with ECR repository created
- GitHub repository secrets configured
Step 1: Create ECR Repository
aws ecr create-repository \
--repository-name myapp \
--image-scanning-configuration scanOnPush=true \
--region us-east-1Note the repository URI: 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp
Step 2: Create IAM User for GitHub Actions
Create an IAM user with minimal ECR permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": "*"
}
]
}Better alternative: Use GitHub Actions OIDC (no long-term keys):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:YOUR_ORG/YOUR_REPO:ref:refs/heads/main"
}
}
}]
}Step 3: Add GitHub Secrets
Go to Settings → Secrets → Actions:
AWS_ACCESS_KEY_ID(if using IAM user)AWS_SECRET_ACCESS_KEY(if using IAM user)AWS_ACCOUNT_ID— your 12-digit AWS account IDAWS_REGION— e.g.,us-east-1SLACK_WEBHOOK_URL— for notifications
Step 4: The GitHub Actions Workflow
Create .github/workflows/build-push.yml:
name: Build and Push to ECR
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
AWS_REGION: ${{ secrets.AWS_REGION }}
ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
ECR_REPOSITORY: myapp
IMAGE_TAG: ${{ github.sha }}
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run unit tests
run: |
# Replace with your test command
npm install
npm test
build-push:
name: Build and Push
runs-on: ubuntu-latest
needs: test # Only run if tests pass
if: github.ref == 'refs/heads/main' # Only on main branch
steps:
- name: Checkout
uses: actions/checkout@v4
- 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: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build Docker image
run: |
docker build \
--tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
--tag $ECR_REGISTRY/$ECR_REPOSITORY:latest \
--cache-from $ECR_REGISTRY/$ECR_REPOSITORY:latest \
.
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
format: table
exit-code: 1 # Fail pipeline on CRITICAL vulnerabilities
severity: CRITICAL
- name: Push to ECR
run: |
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
- name: Notify Slack on success
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "✅ *${{ github.repository }}* deployed to ECR\nImage: `${{ env.IMAGE_TAG }}`\nTriggered by: ${{ github.actor }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "❌ *${{ github.repository }}* build failed\nCommit: `${{ env.IMAGE_TAG }}`"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}Step 5: Optimize Build Speed
Use Docker layer caching with GitHub Actions cache:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push with cache
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:latest
cache-from: type=gha
cache-to: type=gha,mode=maxThis caches Docker layers in GitHub Actions cache — typically cuts build time by 50-70%.
Verify the Pipeline
After pushing to main:
# Check image was pushed
aws ecr list-images \
--repository-name myapp \
--region us-east-1
# Pull and verify
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789.dkr.ecr.us-east-1.amazonaws.com
docker pull 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
docker run --rm 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:latestWhat's Next
This pipeline builds and pushes the image. To complete the CD (deployment) side:
- Update a Kubernetes Deployment image tag automatically
- Trigger ArgoCD to sync the new image
- Use ECS task definition update
See: Build CI/CD with GitHub Actions + ArgoCD + EKS
Resources
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Best DevOps Tools Every Engineer Should Know in 2026
A comprehensive guide to the essential DevOps tools for containers, CI/CD, infrastructure, monitoring, and security — curated for practicing engineers.
Build a Complete CI/CD Pipeline with GitHub Actions + ArgoCD + EKS (2026)
A full project walkthrough — from a simple app to a production-grade GitOps pipeline with automated builds, image scanning, and deployments to AWS EKS using ArgoCD.
Build a DevSecOps Pipeline from Scratch (2026 Project Walkthrough)
A complete end-to-end DevSecOps pipeline with SAST, container scanning, secrets detection, DAST, and supply chain security using open-source tools.