Skip to content

CI/CD on AWS

First PublishedByAtif Alam

AWS provides a suite of developer tools for continuous integration and continuous deployment. You can use all-AWS tools, or integrate AWS with external CI/CD systems like GitHub Actions, GitLab CI, or Jenkins.

┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ CodeCommit │───►│ CodeBuild │───►│ CodeDeploy │───►│ Your App │
│ (source) │ │ (build/test)│ │ (deploy) │ │ (EC2, ECS, │
└──────────────┘ └──────────────┘ └──────────────┘ │ Lambda) │
└──────────────┘
└───────────── CodePipeline (orchestrator) ────────────┘
ServiceRoleComparable To
CodeCommitGit repository hostingGitHub, GitLab
CodeBuildBuild and test (CI)GitHub Actions runner, Jenkins agent
CodeDeployDeploy to EC2, ECS, LambdaArgoCD, Spinnaker
CodePipelineOrchestrate the full pipelineGitHub Actions workflow, Jenkins pipeline

CodeCommit is AWS’s managed Git repository service. It integrates natively with IAM for access control.

Terminal window
# Clone a CodeCommit repo (with IAM credentials helper)
git config --global credential.helper '!aws codecommit credential-helper $@'
git clone https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo

In practice, most teams use GitHub or GitLab instead of CodeCommit (better UI, more features, wider ecosystem). AWS deprecated CodeCommit for new customers in 2024. If you’re starting fresh, use GitHub + AWS integrations.

CodeBuild runs your builds in managed containers — no build servers to maintain. You define the build steps in a buildspec.yml file.

version: 0.2
env:
variables:
APP_ENV: production
secrets-manager:
DB_PASSWORD: prod/db:password # pull from Secrets Manager
phases:
install:
runtime-versions:
python: 3.12
commands:
- pip install -r requirements.txt
pre_build:
commands:
- echo "Running tests..."
- pytest --junitxml=reports/tests.xml
build:
commands:
- echo "Building Docker image..."
- docker build -t my-app:$CODEBUILD_RESOLVED_SOURCE_VERSION .
- docker tag my-app:$CODEBUILD_RESOLVED_SOURCE_VERSION \
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
post_build:
commands:
- echo "Pushing to ECR..."
- aws ecr get-login-password | docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.com
- docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
artifacts:
files:
- imagedefinitions.json # used by CodeDeploy/ECS
discard-paths: yes
reports:
test-results:
files:
- reports/tests.xml
file-format: JUNITXML
cache:
paths:
- '/root/.cache/pip/**/*' # cache pip packages between builds
PhaseWhen It Runs
installInstall dependencies, runtime versions
pre_buildTests, linting, login to registries
buildCompile, build Docker images, package
post_buildPush images, generate deployment artifacts
VariableSource
PlaintextDefined in buildspec.yml or build project config
Secrets Managerenv.secrets-manager in buildspec — never in code
Parameter Storeenv.parameter-store in buildspec
Built-inCODEBUILD_RESOLVED_SOURCE_VERSION (commit SHA), CODEBUILD_BUILD_ID, etc.

CodeDeploy automates deployments to EC2 instances, ECS services, or Lambda functions with rollback support.

StrategyHow It WorksBest For
In-placeStop the app, deploy new version, startEC2 (accepts brief downtime)
Blue/green (EC2)Launch new instances, shift traffic, terminate oldZero-downtime EC2 deployments
Blue/green (ECS)Launch new task set, shift traffic, drain old tasksECS services
Canary (Lambda)Shift 10% traffic, wait, shift remaining 90%Lambda (gradual rollout)
Linear (Lambda)Shift 10% every N minutesLambda (even more gradual)
version: 0.0
os: linux
files:
- source: /
destination: /var/www/myapp
hooks:
BeforeInstall:
- location: scripts/stop_server.sh
timeout: 60
AfterInstall:
- location: scripts/install_deps.sh
timeout: 120
ApplicationStart:
- location: scripts/start_server.sh
timeout: 60
ValidateService:
- location: scripts/health_check.sh
timeout: 120
ApplicationStop → BeforeInstall → Install → AfterInstall →
ApplicationStart → ValidateService

If ValidateService fails, CodeDeploy automatically rolls back.

CodePipeline orchestrates the full CI/CD workflow — connecting source, build, test, and deploy stages.

Source Stage ──► Build Stage ──► Deploy Stage
(GitHub) (CodeBuild) (CodeDeploy / ECS)

Each stage has actions (e.g. “Build with CodeBuild”), and transitions between stages can require manual approval.

Terminal window
aws codepipeline create-pipeline --pipeline '{
"name": "my-app-pipeline",
"roleArn": "arn:aws:iam::123456789012:role/CodePipelineRole",
"stages": [
{
"name": "Source",
"actions": [{
"name": "GitHub",
"actionTypeId": {
"category": "Source",
"owner": "ThirdParty",
"provider": "GitHub",
"version": "2"
},
"configuration": {
"ConnectionArn": "arn:aws:codestar-connections:...",
"FullRepositoryId": "myorg/myrepo",
"BranchName": "main"
},
"outputArtifacts": [{"name": "SourceOutput"}]
}]
},
{
"name": "Build",
"actions": [{
"name": "Build",
"actionTypeId": {
"category": "Build",
"owner": "AWS",
"provider": "CodeBuild",
"version": "1"
},
"inputArtifacts": [{"name": "SourceOutput"}],
"outputArtifacts": [{"name": "BuildOutput"}],
"configuration": {
"ProjectName": "my-app-build"
}
}]
},
{
"name": "Deploy",
"actions": [{
"name": "Deploy",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "ECS",
"version": "1"
},
"inputArtifacts": [{"name": "BuildOutput"}],
"configuration": {
"ClusterName": "production",
"ServiceName": "my-app"
}
}]
}
]
}'

Insert an approval stage before production deployment:

{
"name": "Approval",
"actions": [{
"name": "ManualApproval",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1"
},
"configuration": {
"NotificationArn": "arn:aws:sns:us-east-1:123456789012:approvals",
"CustomData": "Review the staging deployment before promoting to production"
}
}]
}

Many teams prefer GitHub Actions over CodePipeline for its flexibility and developer experience. AWS provides official actions for integration.

.github/workflows/deploy.yml
name: Deploy to ECS
on:
push:
branches: [main]
env:
AWS_REGION: us-east-1
ECR_REPOSITORY: my-app
ECS_CLUSTER: production
ECS_SERVICE: my-app
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # needed for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: ${{ env.AWS_REGION }}
- name: Login to ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image
env:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: task-definition.json
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true

Instead of storing AWS access keys in GitHub secrets, use OIDC federation — GitHub proves its identity to AWS, and AWS issues temporary credentials:

  1. Create an IAM OIDC identity provider for GitHub.
  2. Create a role that trusts the GitHub OIDC provider.
  3. Use aws-actions/configure-aws-credentials with role-to-assume.

No long-lived secrets to rotate or leak.

- name: Deploy Lambda
run: |
zip function.zip -r .
aws lambda update-function-code \
--function-name my-function \
--zip-file fileb://function.zip
- name: Deploy to S3
run: |
aws s3 sync ./dist s3://my-website-bucket --delete
aws cloudfront create-invalidation \
--distribution-id E1ABC2DEF3GH \
--paths "/*"
ApproachBest For
CodePipeline + CodeBuildAWS-centric teams, visual pipeline management, approval gates
GitHub Actions + AWS actionsGitHub-centric teams, more flexible workflows, multi-cloud
GitLab CI + AWSGitLab-centric teams, built-in container registry
Jenkins + AWSComplex pipelines, existing Jenkins infrastructure
ArgoCD (GitOps)Kubernetes-native, declarative deployments
  • CodeBuild runs builds in managed containers — define steps in buildspec.yml. Good for building Docker images and running tests.
  • CodeDeploy handles deployment strategies (in-place, blue/green, canary) with automatic rollback.
  • CodePipeline orchestrates source → build → deploy with optional manual approval gates.
  • GitHub Actions with OIDC is the most popular alternative — no long-lived AWS credentials, great developer experience.
  • Use ECR (Elastic Container Registry) to store Docker images within AWS.
  • Always use IAM roles (not access keys) for CI/CD — whether via instance profiles, OIDC, or execution roles.