We've built and maintained over 500 CI/CD pipelines across our careers. The single biggest difference between teams that ship confidently and teams that dread Fridays? A well-built pipeline they actually trust.
This guide walks you through building a production-grade CI/CD pipeline using GitHub Actions and AWS — the same architecture we deploy for our clients. No toy examples. No "hello world" containers. Real patterns for real production workloads.
Why GitHub Actions + AWS?
Before we dive in, here's why this is our default recommendation for most teams in 2026:
- Zero infrastructure to manage — no Jenkins servers, no patching, no plugin conflicts
- Native GitHub integration — triggers on push, PR, tag, schedule, or any GitHub event
- 2,000 free minutes/month — enough for most small to mid-size teams
- OIDC authentication with AWS — no long-lived access keys, no secret rotation headaches
- Massive marketplace — 20,000+ reusable actions for every tool you use
Architecture Overview
Here's what the complete pipeline looks like:
1. Code Push → Developer pushes to main branch or opens a pull request
2. Build & Test → GitHub Actions runs unit tests, linting, security scans
3. Docker Build → Application is containerized and image pushed to Amazon ECR
4. Deploy → New image deployed to Amazon ECS via blue-green deployment
5. Verify → Health checks pass, traffic shifts to new version
6. Notify → Slack/Teams notification with deployment status and commit details
Step 1: AWS Foundation Setup
Before writing any pipeline code, set up the AWS infrastructure your pipeline will deploy to.
What you need:
- Amazon ECR — a private Docker registry to store your container images
- Amazon ECS (Fargate) — serverless container orchestration, no EC2 instances to manage
- Application Load Balancer — routes traffic and enables blue-green switching
- IAM Role for GitHub — OIDC-based role that GitHub Actions assumes (no access keys)
We define all of this using Terraform so the infrastructure is version-controlled and reproducible. If you're clicking through the AWS console, stop — that's how production environments drift and break.
Step 2: Secure Authentication (OIDC)
This is the most important security decision in the entire pipeline. Never use long-lived AWS access keys in GitHub Actions.
Instead, configure OIDC (OpenID Connect) so GitHub Actions can assume an IAM role directly:
- Create an OIDC Identity Provider in AWS IAM pointing to
token.actions.githubusercontent.com - Create an IAM Role with a trust policy that only allows your specific repository
- Scope the role's permissions to only what the pipeline needs (ECR push, ECS deploy, nothing more)
"Every week we audit client pipelines that still use hardcoded AWS access keys stored in GitHub Secrets. It works, but it's a ticking time bomb. OIDC takes 15 minutes to set up and eliminates the entire class of credential-leak risks."
Step 3: The GitHub Actions Workflow
Here's the structure of a production-grade workflow. We split it into distinct jobs that run in sequence:
Job 1: Test
- Checkout code
- Install dependencies (cached for speed)
- Run unit tests and integration tests
- Run linter (ESLint, Pylint, etc.)
- Run security scanner (Trivy, Snyk, or Grype)
- Fail fast — if any check fails, the pipeline stops here
Job 2: Build & Push
- Authenticate to AWS via OIDC
- Build Docker image with the commit SHA as the tag (never use
latest) - Push to Amazon ECR
- Output the image URI for the deploy job
Job 3: Deploy
- Update ECS task definition with the new image URI
- Trigger ECS service update (blue-green via CodeDeploy or rolling update)
- Wait for deployment to stabilize
- Run smoke tests against the new deployment
Job 4: Notify
- Send Slack/Teams message with deployment result
- Include commit SHA, author, and link to the workflow run
Step 4: Blue-Green Deployment with ECS
Rolling updates are fine for non-critical services. For anything user-facing, we use blue-green deployments through AWS CodeDeploy integrated with ECS.
How it works:
- Blue — current production version, serving all traffic
- Green — new version, deployed to a separate target group
- Test traffic — CodeDeploy routes a small percentage of traffic (or a test listener) to Green
- Health checks — ALB confirms Green is healthy for a configurable period (we use 5 minutes)
- Traffic shift — all traffic moves to Green. Blue stays running for 15 minutes as a rollback safety net
- Cleanup — Blue is terminated after the rollback window closes
If Green fails health checks at any point, CodeDeploy automatically rolls back to Blue. Zero manual intervention. Zero downtime.
Step 5: Pipeline Security Best Practices
A pipeline with access to your production infrastructure is a high-value attack target. Here's how we harden every pipeline we build:
- Pin action versions to SHA — never use
@latestor@v3. Use the full commit SHA to prevent supply-chain attacks - Least-privilege IAM — the pipeline role can push to ECR and update ECS. Nothing else. No S3, no Lambda, no IAM.
- Branch protection — require PR reviews and passing checks before merge to
main - Environment protection rules — production deployments require manual approval from a designated reviewer
- Dependency scanning — Dependabot + Trivy scan for vulnerable packages and container base images
- Secrets masking — GitHub automatically masks secrets in logs, but audit regularly that nothing leaks
Step 6: Caching & Speed Optimisation
A slow pipeline is an unused pipeline. Developers will bypass anything that takes more than 10 minutes. Here's how we keep pipelines under 8 minutes:
- Dependency caching — cache
node_modules,.pip, or.m2between runs. Saves 1–3 minutes per build. - Docker layer caching — use
docker/build-push-actionwith GitHub Actions cache backend. Rebuilds only changed layers. - Parallel jobs — run tests, lint, and security scans in parallel, not sequentially
- Selective triggers — skip the pipeline for docs-only changes using path filters
- Smaller base images — use Alpine or Distroless. Smaller images = faster push to ECR
"Our benchmark for a healthy pipeline: push to main → running in production in under 8 minutes. If your pipeline takes longer, developers start batching changes. Batched changes mean bigger deployments. Bigger deployments mean more risk."
Step 7: Monitoring Your Pipeline
Pipelines break. Dependencies get deprecated. AWS rate-limits your ECR pushes. You need visibility.
Track these metrics:
- Deployment frequency — how often are you shipping? (Target: multiple times per day)
- Lead time for changes — commit to production. (Target: under 1 hour)
- Change failure rate — % of deployments that cause an incident. (Target: under 5%)
- Mean time to recovery (MTTR) — how fast can you rollback? (Target: under 5 minutes)
These are the DORA metrics — the industry standard for measuring DevOps performance. Track them from day one.
Common Mistakes We See
- Using
latesttag for Docker images — you'll never know what's actually running in production. Tag with commit SHA. - No rollback strategy — if you can't rollback in under 5 minutes, you don't have a production pipeline.
- Skipping tests to go faster — the 3 minutes you save will cost you 3 hours debugging a production bug.
- Hardcoded environment values — use SSM Parameter Store or Secrets Manager. Never put database URLs in workflow files.
- No notifications — if a deployment fails and nobody notices, it's worse than not having a pipeline at all.
- One giant workflow file — split into reusable workflows. Your CI and CD should be separate, composable pieces.
GitHub Actions vs Jenkins vs GitLab CI
We get asked this weekly. Here's our honest comparison:
- GitHub Actions — best for teams already on GitHub (90% of teams). Zero maintenance, native integration, generous free tier.
- Jenkins — still relevant for complex on-prem environments with 500+ pipelines. But you're managing servers, plugins, and security patches forever.
- GitLab CI — excellent if your code is on GitLab. The runner model requires more setup than GitHub Actions but offers more control.
For new projects and most existing teams, GitHub Actions is the clear winner in 2026.
Ready to Build Your Pipeline?
A properly built CI/CD pipeline pays for itself within the first month — in developer time saved, in bugs caught before production, and in the confidence to ship on Fridays without fear.
If you're still deploying manually, using SSH to push code, or running a Jenkins server that nobody wants to touch — let's talk. We can audit your current setup and have a production-grade pipeline running within a week.