Amazon ECS CI/CD Pipeline with CodePipeline, CodeBuild, and ECR
An Amazon ECS CI/CD pipeline automates the path from a code commit to a running container in production. This page explains how to wire CodePipeline, CodeBuild, and ECR together to build, push, and deploy Docker images to ECS. You will learn which artifact files connect each stage, how rolling updates differ from blue/green deployments, and what separates a working demo pipeline from one that is safe for real user traffic.
What is an ECS CI/CD pipeline?
Think of it as an automated assembly line for your Docker container. Every time a developer pushes code, the line starts moving: the image gets built, tested, stored in ECR, and shipped to the ECS service running in production. No manual steps, no SSH-ing into servers.
Imagine a factory that gets a new work order every time a bell rings. The bell is a code push to your main branch. The factory floor (CodeBuild) builds the product, labels it with a serial number (the commit SHA tag), and puts it in the warehouse (ECR). A dispatcher (the ECS deploy action) picks it up and delivers it to the storefront (your ECS service). You never have to visit the factory yourself — the process runs automatically every time.
Three AWS services split the work: CodePipeline is the coordinator that moves work through stages and passes files between them. CodeBuild is the builder that runs your tests, builds the Docker image, and pushes it to ECR. The ECS deploy action (or CodeDeploy for blue/green) is the deployer that updates the running service to use the new image.
How an Amazon ECS CI/CD pipeline works
Here is what happens from the moment a developer pushes code to the moment users see the new version:
- Code push. A developer pushes a commit to the main branch of the source repository (GitHub, CodeCommit, or Bitbucket via CodeStar Connections).
- Source trigger. CodePipeline detects the push and starts the pipeline automatically. The source stage downloads the repository and packages it as the first artifact.
- Build and test. CodeBuild picks up the source artifact, runs tests, and builds a Docker image. See building Docker images with CodeBuild for the full build process.
- Image push to ECR. CodeBuild pushes the new image to Amazon ECR tagged with the git commit SHA. Every build produces a uniquely tagged, immutable image. See ECR best practices for tag strategies and lifecycle policies.
- Artifact generation. CodeBuild writes an artifact file (
imagedefinitions.jsonfor rolling update, orimageDetail.jsonfor blue/green) that tells the deploy stage exactly which image to use. This file is the bridge between Build and Deploy. - Deployment to ECS. The deploy stage reads the artifact file and updates the ECS service. Rolling update replaces old tasks gradually. Blue/green via CodeDeploy creates a new task set and shifts ALB traffic to it.
- Health checks. ECS verifies that new tasks start and pass health checks before marking them healthy. For blue/green, CodeDeploy waits for all green tasks to pass before shifting any traffic. If health checks fail, the deployment stops.
- Rollback path. For rolling updates, rollback means manually re-deploying the previous task definition revision. For blue/green, rollback is instant: CodeDeploy shifts traffic back to the still-running blue task set. See rollbacks in CodeDeploy for configuration options.
Prerequisites
Before building the pipeline, the following resources must already exist:
- ECS cluster and service. The ECS service must exist with a task definition that references your container. The pipeline updates the task definition; it does not create the service from scratch.
- ECR repository. CodeBuild needs a private ECR repository to push the Docker image to. See ECR best practices for repository setup and lifecycle policies that prevent unbounded image accumulation.
- Source repository. GitHub, CodeCommit, or another source provider connected to CodePipeline via a CodeStar Connections connection.
- IAM roles. Three separate roles are needed: one for CodePipeline (permission to invoke CodeBuild and access S3), one for CodeBuild (permission to push to ECR and write CloudWatch Logs), and the ECS task execution role (permission to pull from ECR at task start time).
- S3 artifact bucket. CodePipeline stores pipeline artifacts in S3. The console creates this automatically, or you can specify an existing bucket.
- ALB with two target groups (blue/green only). Blue/green deployments require an Application Load Balancer with two target groups registered to the ECS service. CodeDeploy shifts traffic between them. Rolling update does not require a load balancer.
- CodeDeploy application and deployment group (blue/green only). See AWS CodeDeploy overview for setup instructions.
Pipeline architecture overview
A minimal ECS CI/CD pipeline has three stages: Source, Build, and Deploy. Each stage passes artifacts (files one stage produces for the next stage to consume) along the chain. A production-ready pipeline adds a staging deploy and a manual approval gate before production.
┌──────────────────────────────────────────────────────────────┐
│ Source Stage │
│ GitHub / CodeCommit / Bitbucket (push to main) │
│ Output: source_output (source archive) │
└─────────────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────┐
│ Build Stage (CodeBuild) │
│ - Runs tests │
│ - Builds Docker image │
│ - Pushes image to ECR with commit SHA tag │
│ - Writes imagedefinitions.json (or imageDetail.json) │
│ Output: build_output (artifact file) │
└─────────────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────┐
│ [Recommended] Staging Deploy Stage │
│ Deploy to a staging ECS cluster using build_output │
└─────────────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────┐
│ [Recommended] Manual Approval Stage │
│ Pipeline pauses; a reviewer approves before prod continues │
└─────────────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────┐
│ Production Deploy Stage │
│ Option A: ECS rolling update (simpler) │
│ Option B: ECS blue/green via CodeDeploy (safer) │
└──────────────────────────────────────────────────────────────┘The artifact file is the critical handoff between Build and Deploy. It carries the exact image URI that was just pushed to ECR so the deploy action knows which version to run.
Minimal pipeline vs production-ready pipeline
A three-stage pipeline that deploys straight to production works fine for demos and internal tools. For a service with real user traffic, several failure modes are left unhandled. Here is the difference:
| Feature | Minimal (demo / dev) | Production-ready |
|---|---|---|
| Stages | Source, Build, Deploy | Source, Build, Staging, Approval, Production |
| Tests in CodeBuild | Optional or none | Unit tests and integration tests before image push |
| Image tags | :latest (mutable, no history) | Git commit SHA (immutable, fully traceable) |
| Deployment type | Rolling update | Blue/green with CodeDeploy health check hooks |
| Secret handling | Hardcoded env vars or plaintext in buildspec | Secrets Manager or Parameter Store references |
| Manual approval gate | None — every push goes to production | Required before production deploy |
| Rollback | Manual re-deploy of old task definition | Automatic on health check failure or CloudWatch alarm |
| Health verification | ECS default health check only | CodeDeploy lifecycle hooks and CloudWatch alarms |
The minimal pipeline is the right starting point. Build the simplest working pipeline first. Add staging, approval, and blue/green when the service has real users. Each row in the production column is a tradeoff, not a checkbox to clear before deploying anything.
For guidance on multi-environment pipeline design, see managing environments in CI/CD and dev vs staging vs production.
Required deployment artifacts
The artifact file is what the Build stage hands off to the Deploy stage. Which file you need depends on which deploy action you are using.
imagedefinitions.json — ECS rolling update
Used by the standard ECS deploy action (no CodeDeploy involved). It is a JSON array that maps container names to image URIs. The container name must exactly match the container name in your ECS task definition. A case difference or typo causes the deploy to fail or silently use the wrong image.
[
{
"name": "my-app",
"imageUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:abc12345"
}
]For task definitions with multiple containers, include an entry for each container you want to update. The filename is configurable in the ECS deploy action (the default is imagedefinitions.json).
imageDetail.json — ECS blue/green via CodeDeploy
Used by the CodeDeploy deploy action for ECS blue/green deployments. It contains only the image URI; the container name mapping comes from appspec.yml. This file can be written by CodeBuild in the post_build phase, or produced automatically by an Amazon ECR source action when ECR is configured as the pipeline trigger.
{
"ImageURI": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:abc12345"
}appspec.yml — ECS blue/green via CodeDeploy
Also required for CodeDeploy blue/green. This file tells CodeDeploy which task definition to deploy, which container and port to register with the load balancer, and which Lambda functions to run for pre- and post-traffic validation. See AWS CodeDeploy overview for the full appspec.yml structure and lifecycle hooks.
# appspec.yml for ECS blue/green
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <TASK_DEFINITION>
LoadBalancerInfo:
ContainerName: "my-app"
ContainerPort: 8080
Hooks:
- BeforeAllowTraffic: "ValidateBeforeTrafficShift"
- AfterAllowTraffic: "ValidateAfterTrafficShift"The BeforeAllowTraffic and AfterAllowTraffic hooks point to Lambda functions that run health checks. If either Lambda fails, CodeDeploy rolls back automatically. Traffic stays on the original blue task set.
Rolling update uses imagedefinitions.json. Blue/green uses imageDetail.json plus appspec.yml. Using the wrong file for the wrong deploy action is one of the most common causes of pipeline failures. Check which deploy action you configured and match the artifact format exactly.
The CodeBuild buildspec for ECS
This buildspec handles the full build stage: authenticating to ECR, building the image, pushing it, and generating the imagedefinitions.json artifact for a rolling update pipeline. For the CodeBuild fundamentals behind this file, see AWS CodeBuild overview.
version: 0.2
env:
variables:
AWS_DEFAULT_REGION: "us-east-1"
ECR_REPO_NAME: "my-app"
CONTAINER_NAME: "my-app"
phases:
pre_build:
commands:
- AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
- ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com"
- IMAGE_URI="${ECR_REGISTRY}/${ECR_REPO_NAME}"
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-8)
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY
build:
commands:
- docker build -t $IMAGE_URI:$COMMIT_HASH .
post_build:
commands:
- docker push $IMAGE_URI:$COMMIT_HASH
- printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $IMAGE_URI:$COMMIT_HASH > imagedefinitions.json
artifacts:
files:
- imagedefinitions.jsonThis buildspec pushes only the commit-SHA tag, not :latest. Every deployment is traceable to an exact commit. The artifacts block tells CodePipeline to pass imagedefinitions.json to the next stage as the build_output artifact.
Never hardcode secrets in buildspec.yml. Values in env.variables appear in CodeBuild build logs and are visible to anyone with project access. Use env.secrets-manager or env.parameter-store references instead. See secrets in CI/CD pipelines for the correct pattern.
Deployment options: rolling update vs blue/green
Both options get your new image into production. They differ in how much control you have over the traffic shift and how quickly you can undo a bad deploy.
Rolling update is like replacing the bulbs in a string of lights one at a time. The string stays lit the whole time, but for a moment some bulbs are old and some are new. Blue/green is like having a second string of lights ready to go. You plug in the new string and unplug the old one in a single step. If anything looks wrong, plug the old string back in — it never stopped working.
| ECS Rolling Update | ECS Blue/Green (CodeDeploy) | |
|---|---|---|
| How it works | ECS replaces old tasks with new ones gradually by updating the task definition in place | CodeDeploy creates a new task set (green) and shifts ALB traffic from the original (blue) |
| Best for | Internal services, dev/staging, cost-sensitive workloads | Production services with real user traffic or strict uptime SLAs |
| Complexity | Low — no CodeDeploy, no second target group needed | Higher — requires CodeDeploy setup, ALB with two target groups |
| Downtime risk | Low but non-zero; old and new task versions run simultaneously during rollout | Near-zero; green is fully healthy before any user traffic shifts to it |
| Rollback speed | Slow — must re-deploy the previous task definition revision | Instant — shift traffic back to the still-running blue task set |
| Traffic shifting control | None — ECS controls the pace via min/max healthy percent | Full — all-at-once, canary (10% first), or linear increments |
| Deployment artifact | imagedefinitions.json | imageDetail.json + appspec.yml |
| Load balancer required | No (optional for health checks) | Yes — ALB with two target groups is required |
For a deep look at the blue/green strategy including task sets, target groups, and traffic shift configs, see blue/green deployments in AWS. For the mechanics of triggering deploys from CodeBuild output, see deploying with CodeBuild.
When to use an ECS CI/CD pipeline
- Containerized APIs on ECS. Any REST or GraphQL API running in ECS benefits from an automated pipeline. It eliminates manual image pushes and reduces human error on every release.
- Microservices architectures. When you have multiple independent ECS services, each service gets its own pipeline. A change to one service does not block others from deploying.
- Small teams that need production-grade delivery without ops overhead. CodePipeline and CodeBuild are fully managed — no Jenkins server to maintain. A small team can have a working pipeline running in an afternoon.
- Regulated or audited environments. CodePipeline generates an execution history of every pipeline run — which artifact was deployed, when, and who approved it. This is directly useful for SOC 2, PCI, and similar compliance requirements.
- Teams migrating off manual deployments. Even a minimal three-stage pipeline eliminates the most common manual-deploy errors: wrong branch deployed, stale image used, build step skipped.
Common beginner mistakes
- Using
:latestas the only image tag. If every build overwrites:latest, you lose the ability to trace which code is running in which task. Rolling back to a previous task definition revision will pull whatever:latestcurrently points to, not the image from that original deploy. Always tag images with the git commit SHA. - Wrong artifact file for the deployment type. Using
imagedefinitions.jsonwith a CodeDeploy blue/green action, orimageDetail.jsonwith a standard ECS rolling update action, causes the deploy stage to fail or silently ignore the new image. Check which deploy action you configured and match the artifact format exactly. - Mismatched container names. The container name in
imagedefinitions.jsonmust exactly match the container name in the ECS task definition. A case difference or whitespace mismatch causes the deploy action to fail or proceed without updating the image. - Skipping staging in a production pipeline. A pipeline that goes directly from build to production is one bad commit away from a production incident. For any service with real users, add a staging deploy stage and a manual approval gate. See dev vs staging vs production for environment design guidance.
- No health check validation after deploy. ECS checks whether tasks are running. It does not check whether your application is actually serving correct responses. Add CodeDeploy lifecycle hooks or a CloudWatch alarm on 5xx error rates so the pipeline detects broken deployments automatically.
- No rollback plan for rolling updates. Rolling updates have no instant rollback. If you discover a bad deploy after all tasks have been replaced, you must manually trigger a re-deploy with the previous task definition revision. Keep previous image SHA tags and task definition revision numbers documented before this happens. See rollbacks in CodeDeploy for blue/green rollback setup.
- Storing secrets in buildspec.yml. Values hardcoded in
env.variablesappear in CodeBuild build logs and are visible to anyone with access to the project. Useenv.secrets-managerorenv.parameter-storereferences instead. See secrets in CI/CD pipelines for the correct approach. - Under-scoped or over-scoped IAM roles. CodeBuild needs permission to push to ECR and write logs — nothing more. CodePipeline needs permission to invoke CodeBuild and access S3. The ECS task execution role needs permission to pull from ECR. Missing any of these causes access denied errors mid-pipeline. See secure CI/CD pipelines for IAM role design guidance.
Production checklist
Before using this pipeline for a service with real user traffic:
- Images are tagged with the git commit SHA, not only
:latest - Unit and integration tests run in CodeBuild before the image is pushed to ECR
- The pipeline deploys to a staging environment before production
- A manual approval action gates the production deploy
- Deployment uses blue/green with CodeDeploy, or has a documented rollback procedure for rolling update
- Health check hooks or CloudWatch alarms validate the deployment and trigger rollback on failure
- Secrets are sourced from Secrets Manager or Parameter Store, not hardcoded in buildspec
- ECR lifecycle policies are configured to clean up old images and prevent unbounded storage growth
- Pipeline IAM roles follow least privilege — each role has only the permissions it needs
- CodeBuild logs are retained in CloudWatch Logs with an appropriate retention policy
Summary
- An ECS CI/CD pipeline connects Source (GitHub/CodeCommit), Build (CodeBuild), and Deploy (ECS deploy action or CodeDeploy) stages in CodePipeline.
- CodeBuild builds the Docker image, pushes it to ECR with a commit SHA tag, and writes the artifact file that connects Build to Deploy.
- Rolling update uses the standard ECS deploy action and
imagedefinitions.json. Blue/green via CodeDeploy usesimageDetail.jsonplusappspec.yml. - Rolling update is simpler to configure but has slow manual rollback. Blue/green requires more setup but delivers instant rollback and configurable traffic shifting.
- Always tag images with the commit SHA. Using only
:latestbreaks rollback and makes deployment history untraceable. - Production pipelines need a staging stage, manual approval, health check validation, and proper secrets handling. A straight Source to Build to Deploy pipeline is not production-ready.
- CodeDeploy blue/green hooks (
BeforeAllowTraffic,AfterAllowTraffic) run Lambda functions that validate the new task set before and after traffic shifts, enabling automatic rollback on failure.
Frequently asked questions
What does imagedefinitions.json do in an ECS pipeline?
imagedefinitions.json is a JSON file that tells the CodePipeline ECS deploy action which container image to deploy. It maps each container name from your task definition to the full image URI including the tag. CodeBuild typically generates it during the post_build phase and passes it as a pipeline artifact. Without it, the ECS rolling update deploy action does not know which image was just built.
What is the difference between ECS rolling update and blue/green deployment?
Rolling update (native ECS) gradually replaces old tasks with new ones. It is simpler to set up and has no extra infrastructure cost, but rollback is slow because it requires re-deploying the old task definition. Blue/green (via CodeDeploy) creates a new task set behind a separate ALB target group, waits for health checks to pass, then shifts traffic. Rollback is instant because the original blue task set is still running. Blue/green is safer for production; rolling update is easier for dev and lower-risk services.
Does this pipeline work with ECS on Fargate?
Yes. CodePipeline, CodeBuild, and CodeDeploy all work with both ECS on Fargate and ECS on EC2. The pipeline stages and artifact files are identical for both launch types. The only differences are in the ECS task definition itself — Fargate tasks use awsvpc networking and specify CPU and memory at the task level.
Do I need CodeDeploy for ECS CI/CD?
No. CodeDeploy is only required for blue/green deployments. A standard ECS rolling update pipeline uses the built-in ECS deploy action in CodePipeline and does not involve CodeDeploy at all. CodeDeploy becomes necessary when you want traffic shifting between two ALB target groups, configurable canary or linear traffic rollout, or instant rollback by keeping the original task set running.
How do I add a staging environment and manual approval before production?
Add a second Deploy stage to your CodePipeline after the build stage that deploys to a staging ECS cluster. Then add a Manual Approval action between the staging deploy and the production deploy. The pipeline deploys to staging automatically, then pauses and sends an approval notification. A reviewer can inspect staging and approve before the pipeline continues to the production deploy stage.