GitHub Actions Kubernetes Deployment: ECR, GitOps and Production CI/CD
GitHub Actions Kubernetes Deployment: The Decoupled GitOps Architecture
A modern, production-grade delivery pipeline separates Continuous Integration (CI) from Continuous Deployment (CD):
- CI (GitHub Actions): Runs unit/integration tests, compiles code, builds container images with BuildKit, scans for vulnerabilities with Trivy, and pushes immutable signed images to Amazon ECR.
- The Hand-Off: The CI runner commits an updated image tag into a dedicated
infrastructure-gitopsrepository. - CD (ArgoCD inside EKS): ArgoCD detects the new git commit, verifies health checks, and initiates a progressive canary or rolling deployment to Kubernetes.
Keyless AWS Authentication via GitHub OIDC
Establish a trust relationship between your AWS IAM role and GitHub Actions. No static credentials exist to be stolen.
# GitHub Actions workflow step for keyless AWS authentication
- name: Configure AWS Credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::347166961665:role/github-actions-ecr-push-role
aws-region: us-east-1
audience: sts.amazonaws.com
The IAM Role's trust policy restricts access strictly to your specific repository and branch (e.g. repo:org/app:ref:refs/heads/main).
The 970+ Incident Handbook & K8s Runbooks are exclusive to subscribers — unlock for FREE
Get battle-tested production runbooks, AWS architecture blueprints, and zero-downtime CI/CD post-mortems sent directly to your inbox. 100% free, no spam.
BuildKit Caching & Automated Vulnerability Scanning
Building lean, secure containers requires multi-stage Dockerfiles and automated vulnerability gates in the CI workflow:
# Build and scan workflow steps
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and Push to Amazon ECR
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: 347166961665.dkr.ecr.us-east-1.amazonaws.com/api:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan Image for High/Critical Vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: 347166961665.dkr.ecr.us-east-1.amazonaws.com/api:${{ github.sha }}
format: 'table'
exit-code: '1'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
If Trivy detects unmitigated CRITICAL CVEs with available upstream patches, the pipeline fails immediately, preventing vulnerable code from ever reaching the registry.
GitOps Deployment with ArgoCD & Automated Rollbacks
Once the image passes security scanning, the CI workflow updates the image tag in the GitOps repository using kustomize or yq:
cd deploy/overlays/production
kustomize edit set image api=347166961665.dkr.ecr.us-east-1.amazonaws.com/api:${{ github.sha }}
git commit -am "chore(release): update api image to ${{ github.sha }} [skip ci]"
git push origin main
ArgoCD detects the commit and synchronizes the cluster state. Paired with Argo Rollouts, canary deployments can evaluate real-time Prometheus error rates (e.g., HTTP 5xx > 0.2%) and automatically abort and roll back quickly.
Have questions about this architecture or scaling your infrastructure?
Whether you're planning a complex cloud migration, optimizing Kubernetes reliability, or designing autonomous AI workflows, I'm always open to discussing architecture and technical challenges with engineering teams.
Connect with Naveed on LinkedIn →Hardening the Pipeline: Permissions, Environments and Rollback
A pipeline that can push to production is a high-value target, so give it as little power as possible. Set workflow permissions explicitly, request an OIDC token only where it is needed, and bind production deploys to a protected GitHub environment that requires approval.
permissions:
id-token: write # allow the job to request an OIDC token
contents: read
jobs:
build:
runs-on: ubuntu-latest
environment: production # protection rules and approvals live here
steps:
- uses: actions/checkout@<full-commit-sha> # pin actions by commit SHA
- uses: aws-actions/configure-aws-credentials@<full-commit-sha>
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-ecr-push
aws-region: us-east-1
On the AWS side, restrict the role's trust policy to the exact repository and branch or environment through the token's sub claim, so a fork or a different branch cannot assume it. Tag images with the commit SHA or digest instead of latest, scan on push, and promote a release by opening a pull request against the GitOps repository. Rolling back then becomes a git revert that ArgoCD applies for you.
Troubleshooting Common Failures
- Not authorized to perform sts:AssumeRoleWithWebIdentity: the trust policy does not match the token. Check the
subandaudconditions against the actual repository, branch and environment. - Denied when pushing to ECR: the role lacks ECR push permissions or the login step ran for the wrong registry or region.
- ImagePullBackOff after a deploy: the tag was never pushed, the node role cannot pull from ECR, or the manifest points at a stale tag. The Kubernetes runbook walks through each case.
- ArgoCD stuck OutOfSync: something is changing live objects, such as an autoscaler. See how to eliminate ArgoCD configuration drift.
- Slow builds: cache misses are usually caused by layer order or a changing build context, so put dependency installs before frequently changing source.
One last tip for any GitHub Actions Kubernetes deployment: keep the workflow short, move logic into versioned scripts, and review pipeline changes with the same care as application code.
For the infrastructure these pipelines deploy into, see the Terraform AWS architecture.
Frequently Asked Questions
Why should you use AWS IAM OIDC instead of AWS Access Keys in GitHub Actions?
AWS IAM OpenID Connect (OIDC) federates authentication directly between GitHub and AWS, exchanging a short-lived cryptographic JWT token for temporary AWS STS credentials valid for only 1 hour. This eliminates static, long-lived AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY credentials that could be leaked, compromised, or require manual rotation.
Why should CI build pipelines never push directly to Kubernetes with kubectl?
Giving CI pipelines direct cluster admin write access (kubectl apply) violates the principle of least privilege, exposes the cluster control plane to compromised CI runners, and causes configuration drift between git and runtime. The GitOps model with ArgoCD ensures the cluster pulls desired state declaratively from git.
How does Docker BuildKit caching optimize GitHub Actions build times?
Docker BuildKit enables advanced multi-stage caching using the GitHub Actions cache backend (type=gha) or Amazon ECR cache manifests (type=inline). Unchanged application dependency layers (npm, pip, go mod) are retrieved from cache in seconds, reducing build times from 12+ minutes to under 90 seconds.