AWS CodePipeline and CodeBuild: CI/CD on AWS
Published June 2026 · 15 min read
Continuous integration and continuous delivery (CI/CD) on AWS is built around four managed services: CodeCommit (source), CodeBuild (build and test), CodeDeploy (deployment), and CodePipeline (orchestration). Together they form a fully serverless, pay-per-use pipeline that integrates natively with IAM, CloudWatch, S3, and every AWS compute service. This guide walks through each layer in depth — from writing your first buildspec.yml to running zero-downtime blue-green deployments on ECS.
1. AWS CI/CD Suite Overview
AWS provides a purpose-built suite of developer tools that covers every stage of a software delivery pipeline:
- AWS CodeCommit — A fully managed, private Git repository. Stores source code, buildspec files, and appspec files. Supports pull requests, branch policies, and triggers that start CodePipeline automatically. (Note: as of 2024 AWS no longer accepts new customers for CodeCommit; most teams now use GitHub or GitLab as the source stage instead.)
- AWS CodeBuild — A fully managed build service that compiles code, runs unit and integration tests, and produces deployable artifacts. Each build runs in an ephemeral Docker container. No servers to manage, no queues to tune.
- AWS CodeDeploy — An automated deployment service that pushes application revisions to EC2 fleets, Lambda functions, or ECS services. It handles rolling updates, blue-green swaps, and health-check gating.
- AWS CodePipeline — The orchestration layer. A CodePipeline workflow is a series of stages, each containing one or more actions. It passes artifacts between stages using an S3 bucket and reacts to source changes, manual approvals, or schedule triggers.
How They Fit Together
A typical pipeline flow looks like this:
GitHub (push to main)
→ CodePipeline Source stage (fetches repo zip → S3 artifact)
→ CodePipeline Build stage (CodeBuild: compile + unit tests → artifact)
→ CodePipeline Test stage (CodeBuild: integration tests)
→ Manual Approval action (optional gate for production)
→ CodePipeline Deploy stage (CodeDeploy: rolls out to EC2 / ECS)
CodePipeline vs GitHub Actions
GitHub Actions is the dominant CI/CD platform for open-source and mixed-cloud projects. CodePipeline shines when your workloads live entirely in AWS:
- IAM integration — CodeBuild and CodeDeploy assume IAM roles; no secrets stored in CI environment variables for AWS calls.
- VPC support — CodeBuild can run inside your VPC to reach private RDS or ElastiCache endpoints during integration tests.
- Native CodeDeploy — Blue-green ECS deployments with traffic shifting are first-class in CodePipeline; GitHub Actions requires custom scripts.
- Cost model — CodeBuild charges per build minute; no per-seat pricing. For low-frequency builds this is cheaper than GitHub Actions paid tiers.
For teams already on GitHub, the recommended pattern is to use GitHub as the source and trigger CodePipeline via a CodeStar connection (covered in section 4).
2. CodeBuild Deep Dive
CodeBuild reads its instructions from a file named buildspec.yml in the root of your repository (or from a file path you specify in the project configuration). The spec defines phases, environment variables, artifacts to upload, and cache paths.
buildspec.yml Structure
A buildspec has four top-level keys: version, env, phases, and artifacts. Here is a complete example for a Java Maven project:
# buildspec.yml — Java Maven project
version: 0.2
env:
variables:
JAVA_HOME: /usr/lib/jvm/java-21-amazon-corretto
MAVEN_OPTS: "-Xmx1024m"
parameter-store:
DB_PASSWORD: /myapp/prod/db-password # fetched from SSM
secrets-manager:
API_KEY: myapp/prod/api-key:apiKey # fetched from Secrets Manager
phases:
install:
runtime-versions:
java: corretto21
commands:
- echo "Installing dependencies"
- mvn --version
pre_build:
commands:
- echo "Running pre-build checks"
- aws ecr get-login-password --region $AWS_DEFAULT_REGION \
| docker login --username AWS \
--password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/myapp
- IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c1-7)
build:
on-failure: ABORT
commands:
- echo "Building with Maven"
- mvn clean package -DskipTests=false -Dspring.profiles.active=ci
- echo "Building Docker image"
- docker build -t $REPOSITORY_URI:latest -t $REPOSITORY_URI:$IMAGE_TAG .
post_build:
commands:
- echo "Pushing Docker image"
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- echo "Writing image definitions file"
- printf '[{"name":"myapp","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG \
> imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
- target/myapp-*.jar
- appspec.yml
- scripts/**/*
discard-paths: no
cache:
paths:
- /root/.m2/**/* # cache Maven local repo
- /root/.npm/**/* # if project also has npm assets
on-failure: ABORT is set, subsequent phases are skipped. The default is CONTINUE — always set ABORT on the build phase to prevent partial artifacts from being published.
Environment Variables and Secrets
CodeBuild injects several built-in environment variables automatically: CODEBUILD_BUILD_ID, CODEBUILD_RESOLVED_SOURCE_VERSION (the full commit SHA), AWS_DEFAULT_REGION, and AWS_ACCOUNT_ID. You can add custom variables in three ways:
- Plain text in the buildspec
env.variablesblock — fine for non-sensitive config. - SSM Parameter Store via
env.parameter-store— values are fetched at build start; the CodeBuild service role must havessm:GetParameters. - Secrets Manager via
env.secrets-manager— for high-sensitivity credentials; requiressecretsmanager:GetSecretValuein the service role.
VPC Integration
To run integration tests against private resources, configure CodeBuild to launch inside your VPC. Specify the VPC ID, subnets (private, with NAT gateway for internet access), and a security group. The build container gets a private IP and can reach RDS, ElastiCache, or internal services directly.
Build Badge
Every CodeBuild project exposes a publicly accessible badge URL in the format:
https://codebuild.us-east-1.amazonaws.com/badges?uuid=<project-badge-token>&branch=main
Embed this in your README to show live build status. Enable it in the project settings: Additional configuration → Enable build badge.
Custom Build Images
CodeBuild provides managed images for common runtimes (Amazon Linux 2, Ubuntu 22.04, Windows Server 2022). For non-standard toolchains — say, a specific GraalVM version or a custom security scanner — push a Docker image to ECR and reference it in the project environment:
aws codebuild create-project \
--name my-graalvm-project \
--environment \
type=LINUX_CONTAINER,\
computeType=BUILD_GENERAL1_LARGE,\
image=123456789.dkr.ecr.us-east-1.amazonaws.com/graalvm-builder:21,\
imagePullCredentialsType=SERVICE_ROLE \
--service-role arn:aws:iam::123456789:role/CodeBuildServiceRole \
--source type=GITHUB,location=https://github.com/myorg/myapp
3. CodePipeline: Stages, Actions, and Triggers
A CodePipeline pipeline is a directed graph of stages. Each stage runs sequentially; actions within a stage can run in parallel (set runOrder to the same number). Every action produces or consumes artifacts — ZIP files stored in an S3 bucket that CodePipeline manages automatically.
Action Types
- Source — CodeCommit, S3, GitHub (via CodeStar Connection), ECR
- Build — CodeBuild, Jenkins
- Test — CodeBuild, Device Farm
- Deploy — CodeDeploy, ECS, CloudFormation, Elastic Beanstalk, S3
- Approval — Manual gate; sends SNS notification, waits for human sign-off
- Invoke — Lambda function for arbitrary custom logic
Pipeline JSON Definition
Below is a three-stage pipeline (Source → Build → Deploy) defined as a JSON structure you can pass to aws codepipeline create-pipeline:
{
"pipeline": {
"name": "myapp-pipeline",
"roleArn": "arn:aws:iam::123456789:role/CodePipelineServiceRole",
"artifactStore": {
"type": "S3",
"location": "myapp-pipeline-artifacts-123456789",
"encryptionKey": {
"type": "KMS",
"id": "arn:aws:kms:us-east-1:123456789:key/mrk-abc123"
}
},
"stages": [
{
"name": "Source",
"actions": [
{
"name": "GitHubSource",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "CodeStarSourceConnection",
"version": "1"
},
"configuration": {
"ConnectionArn": "arn:aws:codestar-connections:us-east-1:123456789:connection/abc-xyz",
"FullRepositoryId": "myorg/myapp",
"BranchName": "main",
"DetectChanges": "true"
},
"outputArtifacts": [{ "name": "SourceArtifact" }],
"runOrder": 1
}
]
},
{
"name": "Build",
"actions": [
{
"name": "CodeBuild",
"actionTypeId": {
"category": "Build",
"owner": "AWS",
"provider": "CodeBuild",
"version": "1"
},
"configuration": {
"ProjectName": "myapp-build",
"EnvironmentVariables": "[{\"name\":\"ENV\",\"value\":\"production\",\"type\":\"PLAINTEXT\"}]"
},
"inputArtifacts": [{ "name": "SourceArtifact" }],
"outputArtifacts": [{ "name": "BuildArtifact" }],
"runOrder": 1
}
]
},
{
"name": "Deploy",
"actions": [
{
"name": "ProductionApproval",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1"
},
"configuration": {
"NotificationArn": "arn:aws:sns:us-east-1:123456789:pipeline-approvals",
"CustomData": "Review build output before deploying to production."
},
"runOrder": 1
},
{
"name": "CodeDeploy",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CodeDeploy",
"version": "1"
},
"configuration": {
"ApplicationName": "myapp",
"DeploymentGroupName": "myapp-production"
},
"inputArtifacts": [{ "name": "BuildArtifact" }],
"runOrder": 2
}
]
}
]
}
}
runOrder: 1 and the deploy action to runOrder: 2 within the same stage means the deploy only starts after a human approves. This is the recommended pattern for production gates — it keeps both actions visible together in the pipeline visualization.
Cross-Account Pipelines
For enterprise setups with separate AWS accounts for dev, staging, and production, CodePipeline supports cross-account actions. The pipeline lives in the "tools" account; deploy actions assume a cross-account role in each target account. Key requirements: the artifact S3 bucket and KMS key must grant access to the target account's CodeDeploy service role; the pipeline role must have sts:AssumeRole on the cross-account deploy role.
4. GitHub + CodePipeline Integration
AWS recommends the CodeStar Connections approach for GitHub integration. A CodeStar Connection is an OAuth token stored securely in AWS that grants CodePipeline read access to your GitHub repositories. Unlike the older GitHub v1 action (which used a personal access token stored in CodePipeline), CodeStar Connections supports GitHub Apps — a more secure, org-level authorization model.
Creating a CodeStar Connection
# Step 1: Create the connection (starts in PENDING state)
aws codestar-connections create-connection \
--provider-type GitHub \
--connection-name myorg-github-connection \
--region us-east-1
# Step 2: Complete OAuth handshake in the AWS Console
# Navigate to: Developer Tools → Settings → Connections
# Click the PENDING connection → Update pending connection
# Authorize the GitHub App for your org/repositories
# Step 3: Verify it's AVAILABLE
aws codestar-connections get-connection \
--connection-arn arn:aws:codestar-connections:us-east-1:123456789:connection/abc-xyz \
--query ConnectionStatus
Triggering Pipeline on PR Merge
With DetectChanges: true in the Source action configuration (shown in the JSON above), CodePipeline uses an EventBridge rule to detect pushes to the configured branch. Every push to main — including PR merges — starts a new pipeline execution automatically. No webhooks to manage; the EventBridge rule is created and managed by CodePipeline itself.
For branch-based workflows where you want different pipelines for different branches (e.g., a fast unit-test-only pipeline for feature branches), create separate CodePipeline pipelines pointing to the same repository but different branch names. Use pipeline naming conventions like myapp-feature-pipeline and myapp-production-pipeline.
aws codepipeline start-pipeline-execution with OIDC-based authentication.
5. CodeDeploy with EC2: Rolling and Blue-Green
CodeDeploy automates application deployments to EC2 instances by running lifecycle hook scripts defined in appspec.yml. The CodeDeploy agent (a daemon running on each instance) polls for deployment instructions and executes the hooks in order.
appspec.yml for EC2
# appspec.yml — EC2 deployment
version: 0.0
os: linux
files:
- source: /target/myapp.jar
destination: /opt/myapp/
- source: /scripts/
destination: /opt/myapp/scripts/
- source: /config/application-prod.yml
destination: /etc/myapp/
permissions:
- object: /opt/myapp/
owner: myapp
group: myapp
mode: "755"
type:
- directory
- file
hooks:
BeforeInstall:
- location: scripts/stop-app.sh
timeout: 60
runas: root
AfterInstall:
- location: scripts/install-dependencies.sh
timeout: 120
runas: root
- location: scripts/configure-app.sh
timeout: 60
runas: root
ApplicationStart:
- location: scripts/start-app.sh
timeout: 120
runas: myapp
ValidateService:
- location: scripts/health-check.sh
timeout: 60
runas: root
Lifecycle Hook Scripts
A minimal stop-app.sh and start-app.sh pair:
#!/bin/bash
# scripts/stop-app.sh
set -e
if systemctl is-active --quiet myapp; then
systemctl stop myapp
fi
# scripts/start-app.sh
#!/bin/bash
set -e
systemctl daemon-reload
systemctl enable myapp
systemctl start myapp
sleep 5
systemctl is-active myapp || exit 1
# scripts/health-check.sh
#!/bin/bash
set -e
for i in {1..10}; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/health)
if [ "$HTTP_CODE" == "200" ]; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i: got $HTTP_CODE, retrying in 5s..."
sleep 5
done
echo "Health check failed after 10 attempts"
exit 1
Deployment Configurations
CodeDeploy ships with three built-in EC2 deployment configs:
CodeDeployDefault.AllAtOnce— deploys to all instances simultaneously. Fastest but highest risk.CodeDeployDefault.HalfAtATime— deploys to 50% of instances, waits for health checks, then the remaining 50%.CodeDeployDefault.OneAtATime— deploys to one instance at a time. Slowest but safest for small fleets.
Create a custom config for fine-grained control:
aws deploy create-deployment-config \
--deployment-config-name myapp-rolling-25pct \
--compute-platform Server \
--minimum-healthy-hosts type=FLEET_PERCENT,value=75
# Trigger a deployment manually
aws deploy create-deployment \
--application-name myapp \
--deployment-group-name myapp-production \
--deployment-config-name myapp-rolling-25pct \
--s3-location bucket=myapp-artifacts,\
bundleType=zip,\
key=builds/myapp-v2.3.1.zip \
--description "Release v2.3.1 — performance improvements" \
--auto-rollback-configuration enabled=true,\
events=DEPLOYMENT_FAILURE,DEPLOYMENT_STOP_ON_ALARM
--auto-rollback-configuration flag tells CodeDeploy to automatically re-deploy the last successful revision if the new deployment fails or if a CloudWatch alarm fires. This is a safety net — always enable it for production deployment groups.
6. CodeDeploy with ECS: Blue-Green via ALB
ECS blue-green deployments are the most sophisticated CodeDeploy feature. The strategy works by maintaining two ECS task sets behind a single Application Load Balancer. The "blue" task set runs the current version; CodeDeploy creates a "green" task set with the new version, shifts traffic from blue to green, and terminates blue after a configurable wait period.
How the Traffic Shift Works
- CodeDeploy creates the green task set in the ECS service (new task definition version).
- The ALB listener rule is updated to point the test traffic port (e.g., 8080) to the green target group — useful for smoke testing.
- After a configurable delay (or a
ContinueDeploymentAPI call), traffic on the production port (443) shifts from blue to green. - The blue task set is terminated after the termination wait time (default 60 minutes — adjust based on session drain needs).
Traffic Shifting Configurations
ECS CodeDeploy supports three traffic shifting strategies:
- Canary —
CodeDeployDefault.ECSCanary10Percent5Minutes: send 10% of traffic to green for 5 minutes, then shift the remaining 90% at once. Good for catching errors under real load before full rollout. - Linear —
CodeDeployDefault.ECSLinear10PercentEvery1Minutes: increase green's traffic share by 10% every minute over 10 minutes. Best for gradual confidence building. - All-at-once —
CodeDeployDefault.ECSAllAtOnce: shift 100% immediately. Equivalent to a standard ECS rolling update but with instant rollback capability.
To use a canary strategy from CodePipeline, configure the Deploy action provider as CodeDeployToECS and reference the deployment config:
{
"name": "BlueGreenDeploy",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CodeDeployToECS",
"version": "1"
},
"configuration": {
"ApplicationName": "myapp-ecs",
"DeploymentGroupName": "myapp-ecs-production",
"TaskDefinitionTemplateArtifact": "BuildArtifact",
"TaskDefinitionTemplatePath": "taskdef.json",
"AppSpecTemplateArtifact": "BuildArtifact",
"AppSpecTemplatePath": "appspec.yml",
"Image1ArtifactName": "BuildArtifact",
"Image1ContainerName": "IMAGE1_NAME"
},
"inputArtifacts": [{ "name": "BuildArtifact" }],
"runOrder": 1
}
The taskdef.json produced by CodeBuild contains a placeholder <IMAGE1_NAME> that CodePipeline replaces with the actual ECR image URI from the imagedefinitions.json artifact.
See the AWS ECS Containers guide for task definition structure and ECS service configuration details.
7. Full Pipeline via CloudFormation
Defining your pipeline as CloudFormation means it is version-controlled and can be deployed to new environments with a single stack create. The snippet below creates a complete CodePipeline with GitHub source, CodeBuild, and CodeDeploy to EC2:
AWSTemplateFormatVersion: "2010-09-09"
Description: "Techoral — Complete CI/CD Pipeline"
Parameters:
GitHubConnectionArn:
Type: String
Description: CodeStar connection ARN for GitHub
RepositoryId:
Type: String
Default: myorg/myapp
BranchName:
Type: String
Default: main
ArtifactBucketName:
Type: String
Resources:
# ── IAM Roles ──────────────────────────────────────────────
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
RoleName: myapp-codepipeline-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: codepipeline.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess # tighten for production
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: myapp-codebuild-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: codebuild.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/PowerUserAccess
# ── KMS Key for artifact encryption ────────────────────────
ArtifactKey:
Type: AWS::KMS::Key
Properties:
Description: "CodePipeline artifact encryption key"
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Statement:
- Sid: AllowAccountRoot
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action: "kms:*"
Resource: "*"
ArtifactKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/myapp-pipeline-key
TargetKeyId: !Ref ArtifactKey
# ── CodeBuild Project ───────────────────────────────────────
BuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: myapp-build
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_MEDIUM
Image: aws/codebuild/standard:7.0
PrivilegedMode: true # required for Docker builds
EnvironmentVariables:
- Name: AWS_ACCOUNT_ID
Value: !Ref AWS::AccountId
- Name: AWS_DEFAULT_REGION
Value: !Ref AWS::Region
Source:
Type: CODEPIPELINE
BuildSpec: buildspec.yml
Cache:
Type: S3
Location: !Sub "${ArtifactBucketName}/cache/myapp-build"
LogsConfig:
CloudWatchLogs:
Status: ENABLED
GroupName: /aws/codebuild/myapp-build
# ── SNS Topic for failure notifications ────────────────────
PipelineNotificationTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: myapp-pipeline-notifications
Subscription:
- Protocol: email
Endpoint: devops@mycompany.com
# ── CodePipeline ────────────────────────────────────────────
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: myapp-pipeline
RoleArn: !GetAtt CodePipelineRole.Arn
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucketName
EncryptionKey:
Type: KMS
Id: !GetAtt ArtifactKey.Arn
Stages:
- Name: Source
Actions:
- Name: GitHubSource
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection
Version: "1"
Configuration:
ConnectionArn: !Ref GitHubConnectionArn
FullRepositoryId: !Ref RepositoryId
BranchName: !Ref BranchName
DetectChanges: "true"
OutputArtifacts:
- Name: SourceArtifact
RunOrder: 1
- Name: Build
Actions:
- Name: BuildAndTest
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: "1"
Configuration:
ProjectName: !Ref BuildProject
InputArtifacts:
- Name: SourceArtifact
OutputArtifacts:
- Name: BuildArtifact
RunOrder: 1
- Name: Deploy
Actions:
- Name: ApproveProduction
ActionTypeId:
Category: Approval
Owner: AWS
Provider: Manual
Version: "1"
Configuration:
NotificationArn: !Ref PipelineNotificationTopic
CustomData: "Approve to deploy to production."
RunOrder: 1
- Name: DeployToEC2
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CodeDeploy
Version: "1"
Configuration:
ApplicationName: myapp
DeploymentGroupName: myapp-production
InputArtifacts:
- Name: BuildArtifact
RunOrder: 2
# ── EventBridge rule: notify on pipeline failure ────────────
PipelineFailureRule:
Type: AWS::Events::Rule
Properties:
Name: myapp-pipeline-failure-notification
EventPattern:
source: [aws.codepipeline]
detail-type: [CodePipeline Pipeline Execution State Change]
detail:
pipeline: [myapp-pipeline]
state: [FAILED]
State: ENABLED
Targets:
- Arn: !Ref PipelineNotificationTopic
Id: NotifyOnFailure
Outputs:
PipelineName:
Value: !Ref Pipeline
BuildProjectName:
Value: !Ref BuildProject
ArtifactKeyArn:
Value: !GetAtt ArtifactKey.Arn
For CloudFormation fundamentals see the AWS CloudFormation Guide.
8. Pipeline Best Practices
Separate Build, Test, and Deploy Stages
Keep each stage focused on one concern. A common anti-pattern is running integration tests inside the same CodeBuild project as the compile step — if tests are flaky, you lose the compiled artifact and have to rebuild from scratch. Instead: compile and unit-test in the Build stage (fast, always green), run integration tests in a separate Test stage (can be retried without rebuilding), and deploy in the Deploy stage. This structure makes the pipeline graph readable and isolates failure domains.
Artifact Encryption with KMS
By default, CodePipeline encrypts artifacts with an AWS-managed S3 key. For compliance environments (PCI-DSS, HIPAA) use a customer-managed KMS key as shown in the CloudFormation template above. This gives you key rotation control, CloudTrail audit logs for every decrypt operation, and the ability to revoke access instantly by disabling the key.
SNS Notifications on Failure
The EventBridge rule in the CloudFormation template subscribes to FAILED pipeline execution state changes. Extend this to catch stage-level failures by also listening for CodePipeline Stage Execution State Change events with state: FAILED. This way your team gets notified the moment the Build stage fails — rather than waiting for the whole pipeline to be marked failed.
CloudWatch Metrics for Pipeline Health
CodePipeline publishes metrics to CloudWatch under the AWS/CodePipeline namespace: PipelineExecutionAttempts, PipelineExecutionSucceeded, PipelineExecutionFailed. Create a CloudWatch dashboard combining these metrics with your CodeBuild build duration metric (AWS/CodeBuild/Duration) to track pipeline health over time. Alert on a deployment frequency drop — it often signals an unacknowledged failed pipeline that is blocking all deployments.
See the AWS CloudWatch Monitoring guide for alarm and dashboard setup details.
Additional Best Practices
- Cache aggressively — Enable CodeBuild S3 cache for dependency directories (
/root/.m2,node_modules). A cold Java build downloading all Maven dependencies can take 4-6 minutes; with cache it drops to under 90 seconds. - Pin image versions — Use specific CodeBuild image tags (e.g.,
aws/codebuild/standard:7.0) rather thanlatestto ensure reproducible builds. - Use pipeline variables — CodePipeline supports pipeline-level variables that can be passed between stages. Use them to propagate the Git commit SHA from the Source stage to the Deploy stage for tagging deployments in CloudWatch.
- Store buildspec in the repo — Keep
buildspec.ymlin version control alongside the code. This ensures build instructions evolve with the application and enables reproducible historical builds. - Least-privilege IAM roles — The CodePipeline service role needs only
s3:GetObject/PutObjecton the artifact bucket,codebuild:StartBuild, andcodedeploy:CreateDeployment. Avoid broadAdministratorAccessin production. - Cross-region artifacts — For multi-region deployments, configure additional artifact stores per region in the pipeline definition. CodePipeline will replicate artifacts to the target region's S3 bucket automatically.
For IAM role configuration and least-privilege patterns see the AWS IAM Roles and Policies guide. For the S3 artifact bucket setup see the AWS S3 Tutorial.
Frequently Asked Questions
Can I run CodePipeline without CodeDeploy?
Yes. CodePipeline's Deploy stage supports many providers: you can deploy directly to ECS (rolling update), update a CloudFormation stack, publish to an S3 static website, or invoke a Lambda function. CodeDeploy is only required when you need its specific features: lifecycle hooks, deployment groups, blue-green traffic shifting, or deployment rollback policies.
How do I pass values between CodeBuild stages in the same pipeline?
Use exported variables. In your buildspec.yml, add an exported-variables block under env:
env:
exported-variables:
- IMAGE_TAG
- DEPLOYMENT_VERSION
The downstream CodePipeline action can reference these as namespace variables: #{BuildNamespace.IMAGE_TAG}. This avoids writing values to files and re-reading them across stages.
How long can a manual approval action wait?
Manual approval actions time out after 7 days by default. If no reviewer approves or rejects within 7 days, the pipeline marks the action as failed. You can trigger a pipeline restart with the same artifact version after fixing the issue.
What is the difference between CodeDeploy rolling and blue-green for EC2?
Rolling deployment updates instances in-place in batches — the same instances run both old and new code at different points in time. Blue-green deployment provisions a fresh set of EC2 instances with the new version, validates them, then redirects the load balancer. Blue-green is safer (instant rollback by flipping the LB back) but doubles EC2 costs during the deployment window. Use rolling for cost-sensitive workloads, blue-green for critical production services where rollback speed matters.
Can I use CodePipeline with EKS?
Yes, but there is no native EKS deploy action. The typical pattern: use a CodeBuild action in the Deploy stage that runs kubectl apply or helm upgrade against your EKS cluster. The CodeBuild service role needs the eks:DescribeCluster permission and an entry in the EKS aws-auth ConfigMap to authenticate. See the AWS EKS Kubernetes guide for cluster access configuration.
How do I trigger CodePipeline from a Lambda function?
Call codepipeline:StartPipelineExecution from Lambda using the SDK. This is useful for event-driven pipelines — for example, triggering a deployment pipeline when a new artifact is uploaded to S3 (S3 event → Lambda → CodePipeline). Ensure the Lambda execution role has codepipeline:StartPipelineExecution on the pipeline ARN. See the AWS Lambda Serverless guide for Lambda configuration.