GitHub Actions Docker Build and ECR Push
How to build Docker images and push to AWS ECR via GitHub Actions on a self-hosted runner.
GitHub Actions Docker Build and ECR Push
Overview
This workflow builds a Docker image in GitHub Actions and pushes it to AWS ECR. It runs on a self-hosted runner (not ubuntu-latest) and requires Docker to be pre-installed on the runner.
CI/CD Pipeline Flow
Trigger Conditions
on:
push:
branches:
- main
paths:
- 'nextjs/da-claw-journal/**'
- '.github/workflows/ci-da-claw-journal.yml'
Only triggers on:
- Push to
main(not PRs) - Changes to the project directory or workflow file itself
Job Sequence
build β deploy (deploy only runs after build succeeds)
deploy:
needs: build # Wait for build job to pass first
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
Key Steps
1. Checkout Code
- uses: actions/checkout@v6
Get fresh code and git history (needed for commit hash extraction).
2. Install AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -o awscliv2.zip
sudo ./aws/install --update
Downloads and installs the latest AWS CLI. The -o flag overwrites existing files.
3. Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-southeast-1
Uses the official AWS action to set credentials in the runner environment. Credentials stored as GitHub Actions secrets β never shown in logs.
4. Login to ECR
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
ECR_URI="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME}"
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ECR_URI}
- Get AWS account ID via
sts get-caller-identity - Construct ECR repository URI
- Authenticate Docker with ECR
5. Build Docker Image
COMMIT_HASH=$(git rev-parse HEAD)
DEPLOY_TIME=$(date +"%Y-%m-%dT%H:%M:%S%z")
GITHUB_ORG=$(echo "${{ github.repository }}" | cut -d'/' -f1)
GITHUB_PROJECT=$(echo "${{ github.repository }}" | cut -d'/' -f2)
docker build --platform linux/amd64 \
--provenance=false \
--build-arg NEXT_PUBLIC_COMMIT_HASH=${COMMIT_HASH} \
--build-arg NEXT_PUBLIC_DEPLOY_TIME="${DEPLOY_TIME}" \
--build-arg NEXT_PUBLIC_GITHUB_ORG="${GITHUB_ORG}" \
--build-arg NEXT_PUBLIC_GITHUB_PROJECT="${GITHUB_PROJECT}" \
-t ${REPO_NAME}:${IMAGE_TAG} .
Build arguments:
NEXT_PUBLIC_COMMIT_HASHβ Git commit SHA (first 7 chars shown in logs)NEXT_PUBLIC_DEPLOY_TIMEβ ISO 8601 timestamp with timezoneNEXT_PUBLIC_GITHUB_ORGβ Extracted fromgithub.repositorycontextNEXT_PUBLIC_GITHUB_PROJECTβ Repository name without org
Flags:
--platform linux/amd64β Cross-compile for AWS App Runner--provenance=falseβ Skip build provenance (not needed for this use case)
6. Tag and Push
# Latest tag
docker tag ${REPO_NAME}:${IMAGE_TAG} ${ECR_URI}:latest
docker push ${ECR_URI}:latest
# Version tag (YYYY.MM.DD)
docker tag ${REPO_NAME}:${IMAGE_TAG} ${ECR_URI}:${IMAGE_TAG}
docker push ${ECR_URI}:${IMAGE_TAG}
Push two tags:
:latestβ Always points to most recent build:YYYY.MM.DDβ Date-based version tag for rollback
7. Cleanup
if: always() # Run even if previous steps failed
Delete local Docker images to free runner disk space. if: always() ensures cleanup runs regardless of success/failure.
GitHub Secrets Required
Store in repository Settings β Secrets and variables β Actions:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
Note: Never print these in logs. The action handles them securely.
Self-Hosted Runner Requirements
- Docker installed and in PATH
- AWS CLI will be installed by the workflow
- Git installed (for commit hash extraction)
- Network access to ECR repository
What App Runner Gets
When App Runner auto-deploys:
- Latest image always available at
:latesttag - Build metadata baked into image:
- Commit hash in
NEXT_PUBLIC_COMMIT_HASH - Deploy timestamp in
NEXT_PUBLIC_DEPLOY_TIME - GitHub org/project in env variables
- Commit hash in
Common Issues
"docker: command not found"
- Docker not installed on self-hosted runner
- Solution: Install Docker on the runner machine
"ECR login failed"
- AWS credentials not configured correctly
- Solution: Verify
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYsecrets are set
"Image build fails"
- Check
npm run buildpasses locally first - Solution: Fix the build error, push again
See also: Github Actions Monorepo