CI/CD on AWS: Options & Well-Architected Best Practices
Continuous Integration and Continuous Delivery (CI/CD) is the backbone of modern software delivery. AWS provides a rich ecosystem of native and third-party CI/CD tools, and the AWS Well-Architected Framework offers guiding principles to ensure your pipelines are secure, reliable, cost-efficient, and operationally excellent.
This guide covers all the CI/CD options available on AWS, when to use each, and how to align your pipeline architecture with Well-Architected best practices.
AWS Native CI/CD Services
AWS offers a fully managed CI/CD suite that integrates natively with the broader AWS ecosystem. Here's how each service fits into the pipeline:
| Service | Role | Key Features |
|---|---|---|
| CodePipeline | Orchestration | Visual workflow, stage/action model, parallel actions, cross-account/cross-region deployments |
| CodeBuild | Build & Test | Fully managed, pay-per-minute, custom Docker environments, caching, batch builds |
| CodeDeploy | Deployment | EC2, ECS, Lambda targets; blue/green, rolling, canary strategies; automatic rollback |
| CodeCommit | Source (deprecated) | Git hosting (no new customers as of 2024, migrate to GitHub, GitLab, or Bitbucket) |
| CodeArtifact | Artifact Management | Package repositories for npm, PyPI, Maven, NuGet; upstream proxying |
| CodeCatalyst | Unified DevOps | Integrated IDE, CI/CD workflows, issue tracking, dev environments |
CodePipeline: The Orchestrator
CodePipeline defines the stages of your delivery workflow: Source → Build → Test → Deploy. It connects to source providers (GitHub, Bitbucket, S3, ECR), triggers builds, runs tests, requires manual approvals, and deploys to targets.
# Example: CodePipeline with CloudFormation (simplified)
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Stages:
- Name: Source
Actions:
- Name: GitHubSource
ActionTypeId:
Category: Source
Provider: CodeStarSourceConnection
Configuration:
ConnectionArn: !Ref GitHubConnection
FullRepositoryId: "org/repo"
BranchName: main
- Name: Build
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Provider: CodeBuild
Configuration:
ProjectName: !Ref BuildProject
- Name: Deploy
Actions:
- Name: DeployToECS
ActionTypeId:
Category: Deploy
Provider: ECS
Configuration:
ClusterName: !Ref Cluster
ServiceName: !Ref Service
CodeBuild: Build & Test
CodeBuild compiles code, runs tests, and produces artifacts. It scales automatically with no servers to manage. You define build steps in a buildspec.yml file:
version: 0.2
phases:
install:
runtime-versions:
nodejs: 20
pre_build:
commands:
- npm ci
- echo "Logging in to ECR..."
- aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URI
build:
commands:
- npm run test
- npm run build
- docker build -t $ECR_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION .
- docker push $ECR_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION
post_build:
commands:
- echo "Build completed on $(date)"
artifacts:
files:
- imagedefinitions.json
cache:
paths:
- node_modules/**/*
CodeDeploy: Deployment Strategies
CodeDeploy supports multiple deployment strategies depending on your compute platform:
| Strategy | Platform | Description |
|---|---|---|
| In-Place (Rolling) | EC2/On-Premises | Updates instances one batch at a time; brief capacity reduction |
| Blue/Green | EC2, ECS | Provisions new environment, shifts traffic, keeps old as rollback |
| Canary | Lambda, ECS | Routes small % of traffic to new version first (e.g., 10% for 5 min) |
| Linear | Lambda, ECS | Gradually shifts traffic in equal increments over time |
| All-at-Once | Lambda | Immediate full traffic shift, fast but no gradual rollback |
CodeCatalyst: The Unified Experience
Amazon CodeCatalyst is a newer unified DevOps service that combines project management, CI/CD workflows, cloud development environments, and team collaboration in a single tool. It's ideal for teams wanting a GitHub-like experience fully integrated with AWS.
Third-Party CI/CD Options on AWS
Many teams prefer established third-party tools for portability, ecosystem, or familiarity:
| Tool | Hosting Model | Best For |
|---|---|---|
| GitHub Actions | SaaS + self-hosted runners on EC2 | Teams already on GitHub; excellent marketplace of actions |
| GitLab CI/CD | SaaS or self-hosted on EC2/EKS | All-in-one platform; strong security scanning |
| Jenkins | Self-hosted on EC2/EKS | Maximum flexibility; large plugin ecosystem; complex maintenance |
| CircleCI | SaaS + self-hosted runners | Fast builds, good parallelism, Docker-first |
| Terraform Cloud / Spacelift | SaaS | Infrastructure-as-code pipelines specifically |
| Argo CD | Self-hosted on EKS | GitOps-native Kubernetes deployments |
| Flux CD | Self-hosted on EKS | Lightweight GitOps for Kubernetes |
When to Choose Native vs. Third-Party
- Choose AWS Native when you want tight IAM integration, no infrastructure to manage, native EventBridge triggers, and predictable per-pipeline pricing.
- Choose Third-Party when you need multi-cloud portability, your team already has expertise in a tool, or you require advanced features like matrix builds or complex DAG pipelines.
- Hybrid approach is common: use GitHub Actions for CI (build/test) and CodeDeploy or CDK Pipelines for CD (deployment to AWS).
Well-Architected Best Practices for CI/CD
The AWS Well-Architected Framework defines six pillars. Here's how each applies to CI/CD pipeline design:
🏗️ Operational Excellence
"Make frequent, small, reversible changes."
- Automate everything: no manual steps between commit and production
- Use infrastructure as code (CloudFormation, CDK, Terraform) for pipeline definitions
- Implement observability: pipeline metrics, build dashboards, failure alerting
- Practice runbook automation for rollback procedures
- Version control your
buildspec.yml, pipeline definitions, and deployment configs - Run pipelines in response to events (push, PR, tag) not on schedules
🔒 Security
"Apply security at all layers."
- Use IAM roles (not access keys) for pipeline service accounts with least-privilege
- Store secrets in AWS Secrets Manager or SSM Parameter Store, never in code or env vars
- Enable pipeline encryption: S3 artifact buckets with KMS, encrypted build environments
- Integrate SAST/DAST scanning in build stage (CodeGuru, Snyk, Trivy, Checkov)
- Sign container images and validate signatures before deployment
- Use VPC endpoints for CodeBuild to prevent internet egress
- Enable CloudTrail logging on all CI/CD API actions
- Implement cross-account deployment with assume-role patterns (dev → staging → prod)
⚡ Reliability
"Automatically recover from failure."
- Configure automatic rollback on deployment failure (CloudWatch alarms + CodeDeploy)
- Use blue/green or canary deployments to limit blast radius
- Run integration tests and smoke tests as post-deployment validation
- Design pipelines to be idempotent: re-running should be safe
- Implement circuit breakers: halt pipeline if error rate exceeds threshold
- Use multi-AZ / multi-region artifact storage for DR
- Set timeouts on all build and deploy stages to prevent hung pipelines
🚀 Performance Efficiency
"Use computing resources efficiently."
- Cache dependencies (npm, pip, Maven) in CodeBuild to reduce build times
- Use CodeBuild batch builds for parallel test execution
- Right-size build compute: use
BUILD_GENERAL1_SMALLfor simple builds, scale up for Docker builds - Use Lambda compute type in CodeBuild for sub-second startup on lightweight builds
- Implement incremental builds: only build what changed (monorepo path filters)
- Store build artifacts in S3 with lifecycle policies to expire old versions
💰 Cost Optimization
"Avoid unnecessary costs."
- CodeBuild: pay only for build minutes consumed, no idle costs
- Avoid over-provisioned self-hosted runners; use spot instances for Jenkins agents
- Use CodePipeline V2 (per-action pricing) for infrequently-triggered pipelines vs. V1 (per-pipeline/month)
- Set S3 lifecycle rules on artifact buckets (delete after 30 days)
- Consolidate builds: use CodeBuild batch mode instead of separate projects
- Tag all CI/CD resources for cost allocation and visibility
- Review and delete orphaned pipelines and build projects monthly
🌱 Sustainability
"Minimize environmental impact."
- Use managed services (serverless builds) instead of always-on EC2 instances
- Cache aggressively to reduce redundant compute work
- Use ARM-based build instances (
BUILD_GENERAL1_SMALLwith ARM image) for lower energy consumption - Minimize artifact sizes: use multi-stage Docker builds, strip debug symbols
- Run pipelines only when needed: avoid unnecessary scheduled builds
Pipeline Architecture Patterns
Pattern 1: Single Account (Simple)
For small teams or non-production workloads:
Developer → GitHub → CodePipeline → CodeBuild → CodeDeploy → EC2/ECS
↓
S3 Artifacts
Pattern 2: Cross-Account (Enterprise)
Recommended for production workloads with proper environment isolation:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Dev Account │ │ Staging Acct │ │ Prod Account│
│ │ │ │ │ │
│ CodeBuild │───▶│ CodeDeploy │───▶│ CodeDeploy │
│ Unit Tests │ │ Int. Tests │ │ Blue/Green │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
│ ┌─────────────┐ │
└───────────│ Tools Account│────────────┘
│ CodePipeline │
│ Artifact S3 │
│ KMS Key │
└─────────────┘
Key elements of cross-account deployment:
- Central tools account owns the pipeline and artifact bucket
- Cross-account IAM roles with least-privilege for each target account
- KMS CMK shared via key policy to encrypt/decrypt artifacts across accounts
- Manual approval gate before production deployment
Pattern 3: GitOps with EKS
Developer → GitHub PR → GitHub Actions (CI)
↓
Build & push to ECR
↓
Update manifest repo
↓
Argo CD (in EKS) syncs desired state
↓
Kubernetes applies changes
Pattern 4: CDK Pipelines (Self-Mutating)
AWS CDK Pipelines is a high-level construct that creates a self-mutating pipeline: the pipeline updates itself when you change its definition:
import { CodePipeline, ShellStep, CodePipelineSource } from 'aws-cdk-lib/pipelines';
const pipeline = new CodePipeline(this, 'Pipeline', {
synth: new ShellStep('Synth', {
input: CodePipelineSource.gitHub('org/repo', 'main'),
commands: ['npm ci', 'npx cdk synth'],
}),
});
pipeline.addStage(new StagingStage(this, 'Staging'));
pipeline.addStage(new ProductionStage(this, 'Prod'), {
pre: [new ManualApprovalStep('PromoteToProd')],
});
Security Hardening Checklist
- Pipeline permissions: Each stage should have its own IAM role scoped to only the actions it performs
- Artifact encryption: Use a customer-managed KMS key for the S3 artifact bucket
- Network isolation: Run CodeBuild in a VPC with no internet gateway; use VPC endpoints for AWS services
- Dependency scanning: Scan third-party libraries for vulnerabilities during the build phase
- Container scanning: Use ECR image scanning or Trivy before promoting images
- Branch protection: Require PR reviews and passing CI before merge to main
- Audit trail: Enable CloudTrail for pipeline API calls and S3 data events on artifact buckets
- Secrets rotation: Rotate service credentials automatically via Secrets Manager
Choosing the Right Approach: Decision Matrix
| Scenario | Recommended Approach |
|---|---|
| Small team, all-in on AWS, simple app | CodePipeline + CodeBuild + CodeDeploy |
| Team uses GitHub heavily | GitHub Actions for CI → CodeDeploy or CDK Pipelines for CD |
| Kubernetes-native workloads on EKS | Argo CD or Flux CD (GitOps) |
| Multi-cloud or vendor-neutral requirement | GitLab CI/CD or GitHub Actions |
| Enterprise with strict compliance | Cross-account CodePipeline + manual approvals + audit logging |
| Infrastructure-only changes (IaC) | CDK Pipelines or Terraform Cloud |
| Serverless (Lambda) deployments | SAM Pipelines or CDK Pipelines |
| Legacy app migration to CI/CD | Jenkins on EC2 (short-term) → CodePipeline (long-term) |
Key Takeaways
- Start with managed services: CodePipeline + CodeBuild eliminates infrastructure overhead and integrates natively with IAM, CloudWatch, and EventBridge.
- Adopt cross-account deployment: Separate dev, staging, and production accounts for blast radius reduction and least-privilege enforcement.
- Automate security: Embed SAST, container scanning, and dependency checks directly in the pipeline. Don't bolt security on after the fact.
- Use progressive deployments: Canary and blue/green strategies give you confidence and automatic rollback capability.
- Align with Well-Architected: Every pipeline decision maps to at least one pillar. Revisit your pipeline design as part of regular Well-Architected Reviews.
- Measure pipeline health: Track deployment frequency, lead time, change failure rate, and mean time to recovery (the DORA metrics).
A well-designed CI/CD pipeline on AWS isn't just about shipping code faster. It's about shipping safely, securely, and sustainably while maintaining the agility your business demands.
References:
AWS Well-Architected Framework ·
Operational Excellence Pillar ·
Choosing a Well-Architected CI/CD Approach (AWS Blog)