Blue/Green CI/CD Pipeline on ECS Fargate
Build a container CI/CD pipeline with zero-downtime blue/green deployments.
Introduction
30 Second Summary
Every time you refresh a website and see new features without any downtime, a deployment strategy is working behind the scenes. The old version keeps running while a new version quietly takes over.
In this project, you will build a complete container CI/CD pipeline on AWS that automatically builds, pushes, and deploys a containerized web application with zero-downtime blue/green deployments.
What You'll Build
A fully automated pipeline that builds Docker images, pushes them to Amazon ECR, and deploys them to ECS Fargate using CodeDeploy's blue/green strategy behind an Application Load Balancer.
By the end of this project, you'll have:
- A GitHub repository with your Dockerfile, build specification, and deployment configuration files that drive the entire pipeline.
- An ECS Fargate cluster running your containerized application behind an Application Load Balancer with two target groups for blue/green traffic routing.
- A CodePipeline V2 pipeline that automatically builds Docker images, pushes them to ECR, and triggers CodeDeploy for zero-downtime blue/green deployments.
- Secret Mission: Add automated container health check testing as a quality gate before deployment.
Are there any prerequisites?
This project assumes you have an AWS account with admin access, a GitHub account, and the AWS CLI already installed on your Mac. You should also have a basic understanding of Docker and familiarity with AWS services like IAM and EC2.
Prior experience with CodeBuild and CodeDeploy (EC2-based) is helpful but not strictly required.
Before We Start
Before We Start...
Install Docker Desktop and Set Up Your Repository
Before you can build container images and push them through a pipeline, your local machine needs the right tools.
In this step, you will install Docker Desktop for building container images, verify your AWS CLI is ready to talk to AWS, and create the GitHub repository that will hold your project files.
In this step, get ready to:
- Install Docker Desktop and confirm it is running.
- Verify your AWS CLI configuration and default region.
- Create and clone a GitHub repository for your pipeline source code.
Download and install Docker Desktop
Docker Desktop is the application that lets you build and run container images on your Mac. Your pipeline will use CodeBuild to build images in the cloud, but you need Docker locally to test your image before pushing it to Amazon ECR.
- Open your browser and navigate to docker.com/products/docker-desktop.
- Click Download Docker Desktop for Mac.
- Open the downloaded Docker.dmg file.
- Drag the Docker icon into your Applications folder.
- Press Cmd+Space to open Spotlight Search.
- Type Docker and press Enter to launch Docker Desktop.
- Accept the Docker Desktop service agreement when prompted.
Wait for the Docker icon in your menu bar to stop animating. Once it shows a steady whale icon, Docker is ready.
- Open your terminal by pressing Cmd+Space, typing Terminal, and pressing Enter.
- Verify Docker is installed by running this command:
docker --version
You should see output like Docker version 27.x.x (the exact number may differ).
- Confirm Docker can communicate with the Docker daemon by running:
docker info
You should see a wall of system information including Server Version, Storage Driver, and Operating System.
Seeing 'Cannot connect to the Docker daemon'?
This means Docker Desktop has not finished starting up. Check the Docker whale icon in your menu bar. If it is still animating, wait 30 seconds and try again.
If the icon is not there at all, relaunch Docker Desktop from your Applications folder.
help me troubleshoot Docker Desktop not connecting on my Mac
Verify your AWS CLI configuration
Your pipeline will create AWS resources like ECR repositories and ECS clusters from the command line. The AWS CLI needs to be configured with credentials and a default region before you can do that.
- Check that your AWS CLI can authenticate by running:
aws sts get-caller-identity
You should see a JSON response containing your Account ID, UserId, and Arn.
✔️ I see my Account ID and ARN
Your AWS CLI is authenticated and ready to go.
ⓧ I see 'Unable to locate credentials'
Your CLI does not have credentials configured. Run the following command and enter your AWS access key ID, secret access key, default region, and output format when prompted:
aws configure
After entering your credentials, run aws sts get-caller-identity again to confirm it works.
- Confirm your default region is set by running:
aws configure get region
You should see a region value like us-east-1. If nothing appears, set your region by running:
aws configure set region us-east-1
Why us-east-1?
This project works in any region, but us-east-1 is the default for most AWS tutorials and has the broadest service availability. If you already have resources in a different region, use that one instead.
Create your GitHub repository and clone it locally
Your pipeline needs a source repository to watch for changes. When you push code to this repository, CodePipeline V2 detects the change and kicks off the build and deploy process.
- Open github.com in your browser and sign in.
- Click the + icon in the upper-right corner of the page.
- Select New repository.
- Enter ecs-blue-green-app as the Repository name.
- Leave the visibility set to Public.
- Check the box for Add a README file to initialize the repository with a default branch.
- Click Create repository.
Why initialize with a README?
Initializing with a README creates a main branch immediately. Without it, the repository has no branch until your first push, which makes cloning and pipeline setup more complicated.
Now clone the repository to your local machine so you can add files to it.
- Move to your Desktop by running this command:
cd ~/Desktop
- Clone your new repository by running:
git clone [[YOUR_REPO_URL="https://github.com/YOUR_USERNAME/ecs-blue-green-app.git"]]
Find your clone URL
On your repository page, click the green Code button and copy the HTTPS URL.
- Move into the cloned repository folder by running:
cd ecs-blue-green-app
- Confirm you are on the main branch by running:
git branch
You should see * main in the output, confirming you are on the correct branch.
Git clone failing with authentication errors?
GitHub no longer accepts password authentication for HTTPS. You need either a personal access token or SSH key configured. If prompted for a password, use a personal access token instead.
help me set up GitHub authentication so I can clone my repository
Your local environment is locked and loaded. Next up, you will build your first container image and push it to Amazon ECR.
Build Your Container Image and Push to ECR
You have Docker, the AWS CLI, and a fresh GitHub repository ready to go. Now it's time to build the actual application your pipeline will deploy.
Every CI/CD pipeline needs something to deploy. In this step, you'll create a containerized web application, set up a private container registry on AWS to store it, and push your first Docker image.
In this step, get ready to:
- Create a Dockerfile and HTML page for your web application.
- Create an Amazon ECR repository to store your Docker images.
- Build, tag, and push your Docker image to ECR.
Create your web application files
Your pipeline will deploy a simple web page served by Nginx inside a container. You need two files: an index.html page and a Dockerfile that packages it into an image.
- Make sure your terminal is inside the ecs-blue-green-app directory you cloned in the previous step. Navigate there by running:
cd ~/Desktop/ecs-blue-green-app
- Create the index.html file by running:
touch index.html
- Open index.html in your code editor and paste in the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ECS Blue/Green App</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #ffffff;
}
.container {
text-align: center;
padding: 2rem;
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.version {
font-size: 1.2rem;
color: #4ecca3;
margin-top: 1rem;
}
.status {
margin-top: 2rem;
padding: 1rem;
background: rgba(78, 204, 163, 0.1);
border: 1px solid #4ecca3;
border-radius: 8px;
}
</style>
</head>
<body>
<div class="container">
<h1>ECS Blue/Green Deployment</h1>
<p>This application is deployed via CodePipeline with zero-downtime blue/green deployments.</p>
<div class="version">Version 1.0</div>
<div class="status">
<p>Deployment Status: Live</p>
<p>Platform: AWS ECS Fargate</p>
</div>
</div>
</body>
</html>
What does this HTML do?
This is a styled landing page that displays the deployment status and version number. Later in the project, you'll change the version number and push again to prove the blue/green deployment works with zero downtime.
- Save index.html.
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ECS Blue/Green App</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #ffffff;
}
.container {
text-align: center;
padding: 2rem;
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.version {
font-size: 1.2rem;
color: #4ecca3;
margin-top: 1rem;
}
.status {
margin-top: 2rem;
padding: 1rem;
background: rgba(78, 204, 163, 0.1);
border: 1px solid #4ecca3;
border-radius: 8px;
}
</style>
</head>
<body>
<div class="container">
<h1>ECS Blue/Green Deployment</h1>
<p>This application is deployed via CodePipeline with zero-downtime blue/green deployments.</p>
<div class="version">Version 1.0</div>
<div class="status">
<p>Deployment Status: Live</p>
<p>Platform: AWS ECS Fargate</p>
</div>
</div>
</body>
</html>
- Now create the Dockerfile by running:
touch Dockerfile
- Open Dockerfile in your code editor and paste in the following content:
# Use the lightweight Nginx Alpine image as the base
FROM nginx:alpine
# Copy our custom HTML page into Nginx's default serving directory
COPY index.html /usr/share/nginx/html/index.html
# Expose port 80 for HTTP traffic
EXPOSE 80
# Start Nginx in the foreground so the container stays running
CMD ["nginx", "-g", "daemon off;"]
What does this Dockerfile do?
This Dockerfile packages your web page into a container image. It starts from the official Nginx Alpine image (a lightweight web server), copies your index.html into the default serving directory, exposes port 80, and starts Nginx.
- Save Dockerfile.
- Confirm both files exist by running:
ls
You should see Dockerfile and index.html in the output.
Don't see both files?
Make sure you ran the touch commands from inside the ecs-blue-green-app directory. Run pwd to check your current directory ends with ecs-blue-green-app.
help me find my Dockerfile and index.html files
✔️ Awesome, I've got everything!
Great. Double check you've saved both files before moving on.
ⓧ I'd like to double check the full code
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Create an ECR repository and authenticate Docker
Your Docker image needs a home on AWS. Amazon ECR (Elastic Container Registry) is a fully managed container registry that stores, manages, and deploys Docker images. Your pipeline will pull images from this registry during deployments.
- Create the ECR repository by running this command:
aws ecr create-repository --repository-name ecs-blue-green-app
What does this command do?
This creates a private ECR repository named ecs-blue-green-app in your default AWS region. The command returns a JSON response with the repository ARN, name, and URI. Note down the repositoryUri from the output. It follows the format ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/ecs-blue-green-app.
You should see JSON output containing a repositoryUri field. This is your image's address on AWS.
- Authenticate Docker to your ECR registry by running this command:
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin [[AWS_ACCOUNT_ID="your 12-digit AWS account ID"]].dkr.ecr.us-east-1.amazonaws.com
What does this command do?
This command generates a temporary authentication token from AWS and pipes it into docker login. The token lasts 12 hours. After running it, Docker can push and pull images from your private ECR registry.
You should see Login Succeeded in your terminal output.
Seeing "Error: Cannot perform an interactive login"?
Make sure Docker Desktop is running. Check for the Docker whale icon in your menu bar. If it's not there, open Docker Desktop and wait for it to say "Docker is running" before trying again.
Also verify you replaced the account ID placeholder with your actual 12-digit AWS account ID. You can find it by running aws sts get-caller-identity.
help me fix my ECR login error
Build, tag, and push your Docker image
With your ECR repository ready and Docker authenticated, you can now build the image locally, tag it with the ECR repository URI, and push it up to AWS.
- Build your Docker image by running this command from the ecs-blue-green-app directory:
docker build -t ecs-blue-green-app .
What does this command do?
The -t flag tags the image with the name ecs-blue-green-app. The . at the end tells Docker to use the current directory as the build context (where it finds your Dockerfile and index.html).
You should see Docker download the Nginx Alpine base image and then output Successfully tagged ecs-blue-green-app:latest.
- Tag the image with your ECR repository URI by running:
docker tag ecs-blue-green-app:latest [[AWS_ACCOUNT_ID="your 12-digit AWS account ID"]].dkr.ecr.us-east-1.amazonaws.com/ecs-blue-green-app:latest
- Push the image to ECR by running:
docker push [[AWS_ACCOUNT_ID="your 12-digit AWS account ID"]].dkr.ecr.us-east-1.amazonaws.com/ecs-blue-green-app:latest
You should see Docker push each layer of the image. The final output shows the image digest and latest: digest: sha256:... confirming the push succeeded.
Seeing "denied: Your authorization token has expired"?
ECR login tokens expire after 12 hours. Re-run the aws ecr get-login-password command from the previous substep, then try the push again.
If you see name unknown: The repository does not exist, double check that your account ID and region in the tag match what you used to create the repository.
help me push my Docker image to ECR
- Verify the image appears in the ECR console. Open the AWS Management Console in your browser.
- Search for ECR in the top search bar and select **Elastic Container Registry**.
- Click on **Private repositories** in the left sidebar.
- Click on the ecs-blue-green-app repository name.
- You should see one image with the tag latest listed in the repository.
Your container image is now safely stored in ECR. Next up, you'll create the Application Load Balancer and target groups that will handle traffic routing during blue/green deployments.
Create the Application Load Balancer and Target Groups
Your Docker image is sitting in ECR, ready to serve traffic. Before you can run it on ECS with blue/green deployments, you need a way to route traffic between the old and new versions of your application.
An Application Load Balancer with two target groups makes this possible. One target group handles production traffic, and the other handles test traffic during deployments. CodeDeploy uses these two target groups to shift traffic from the old (blue) version to the new (green) version without downtime.
In this step, get ready to:
- Create an Application Load Balancer with two listeners (port 80 and port 8080).
- Create two target groups for blue/green traffic routing.
- Update the security group to allow inbound traffic on both ports.
Create the Application Load Balancer
The ALB sits in front of your ECS containers and distributes incoming requests. For blue/green deployments, you need two listeners: port 80 for production traffic and port 8080 for testing the new version before it goes live.
- Sign in to the AWS Management Console.
- In the search bar at the top, type EC2 and select EC2 from the results.
- In the left navigation pane, scroll down and click Load Balancers (under the Load Balancing section).
- Click Create load balancer.
- Under Application Load Balancer, click Create.
You are now on the ALB configuration page. Configure the basic settings:
- For Load balancer name, enter ecs-bluegreen-alb.
- For Scheme, select Internet-facing.
- For IP address type, keep IPv4 selected.
Next, configure the network mapping:
- For VPC, select your default VPC.
- Under Mappings, select at least two Availability Zones by checking the boxes next to them.
- For each selected AZ, choose a public subnet from the dropdown.
Why two Availability Zones?
ALBs require subnets in at least two different Availability Zones for high availability. If one AZ goes down, the other continues serving traffic.
Leave the security group section as-is for now. You will update it after the ALB is created.
Create the first target group and listener
Before you can finish creating the ALB, you need at least one target group for its default listener. You will create the first target group directly from the ALB creation page.
- Scroll down to the Listeners and routing section.
- You should see a default listener on HTTP : 80. Under Default action, click Create target group. This opens a new browser tab.
In the new tab, configure the first target group:
- For Choose a target type, select IP addresses.
- For Target group name, enter ecs-tg-1.
- For Protocol, keep HTTP selected.
- For Port, keep 80.
- For VPC, select your default VPC.
- Leave the health check settings at their defaults.
- Click Next.
- Skip registering targets (ECS will handle this automatically). Click Create target group.
Why IP address target type?
ECS Fargate uses the awsvpc network mode, which assigns each task its own IP address. The target type must be IP addresses (not Instances) so the ALB can route directly to Fargate tasks.
- Switch back to the ALB creation tab in your browser.
- Click the refresh icon next to the Default action dropdown for the port 80 listener.
- Select ecs-tg-1 from the dropdown.
Now add the second listener for test traffic:
- Click Add listener.
- For Protocol, keep HTTP.
- For Port, change it to 8080.
You need a second target group for this listener. Create it now:
- Under the port 8080 listener's Default action, click Create target group. This opens another new tab.
- For Choose a target type, select IP addresses.
- For Target group name, enter ecs-tg-2.
- For Protocol, keep HTTP.
- For Port, change it to 8080.
- For VPC, select your default VPC.
- Leave the health check settings at their defaults.
- Click Next.
- Skip registering targets. Click Create target group.
- Switch back to the ALB creation tab.
- Click the refresh icon next to the Default action dropdown for the port 8080 listener.
- Select ecs-tg-2 from the dropdown.
Scroll to the bottom of the page and click Create load balancer.
You should see a success message confirming that your ALB has been created. The ALB will take a couple of minutes to transition from provisioning to active state.
What are these two listeners for?
Port 80 is your production listener. This is where real user traffic goes.
Port 8080 is your test listener. During a blue/green deployment, CodeDeploy routes a small amount of traffic here first so you can verify the new version before switching production traffic over.
ALB creation failed?
- Make sure you selected at least two subnets in different Availability Zones. ALBs require a minimum of two AZs.
- Ensure the subnets you selected are public subnets (they have a route to an internet gateway). The default VPC subnets are public by default.
- If the name is rejected, confirm it is exactly ecs-bluegreen-alb with no extra spaces or special characters.
Help me troubleshoot my ALB creation error.
Update the security group to allow traffic on both ports
By default, the ALB's security group may not allow inbound traffic on port 8080. You need to update it so both production and test traffic can reach your application.
- In the Load Balancers list, click on ecs-bluegreen-alb to open its details page.
- Scroll down to the Security tab.
- Click on the security group ID link (it starts with sg-). This opens the security group details.
- Note down this security group ID. You will need it when creating the ECS service in the next step.
- Click the Inbound rules tab.
- Click Edit inbound rules.
Add rules to allow traffic on both ports:
- If a rule for port 80 does not already exist, click Add rule. Set Type to HTTP (this auto-fills port 80). Set Source to Anywhere-IPv4 (0.0.0.0/0).
- Click Add rule again. Set Type to Custom TCP. Set Port range to 8080. Set Source to Anywhere-IPv4 (0.0.0.0/0).
- Click Save rules.
Why open port 8080?
Port 8080 is the test listener. During a blue/green deployment, CodeDeploy sends test traffic here so you can verify the new version is healthy before it shifts production traffic on port 80.
- Navigate back to your ALB by clicking Load Balancers in the left navigation pane.
- Click on ecs-bluegreen-alb and find the DNS name in the Description section. Copy it and save it somewhere. You will need this URL to verify your application later.
Keep the security group ID handy
You will need the ALB security group ID (sg-...) in the next step when creating the ECS service. Paste it in a text editor or note-taking app so you can find it quickly.
Can't access port 8080 later?
- Double check that your inbound rules show both port 80 and port 8080 with source 0.0.0.0/0.
- Make sure you edited the correct security group. The one attached to your ALB (not a different security group in your account).
Help me fix my ALB security group rules.
Your load balancer is ready to route traffic. Next up, you will create the ECS cluster and service that actually runs your containers behind this ALB.
Create the ECS Cluster and Service
Your load balancer is ready and waiting for traffic. Now you need something to send traffic to.
In this step, you will create an ECS cluster, register a task definition that describes your container, and launch an ECS service that keeps your container running behind the ALB.
In this step, get ready to:
- Create an ECS cluster and the required IAM role.
- Create and register a task definition for your container.
- Launch an ECS service with blue/green deployment controller.
Create the ECS cluster and IAM execution role
An ECS cluster is a logical grouping for your tasks and services. Fargate handles the underlying infrastructure, so all you need is the cluster itself and an IAM role that gives ECS permission to pull your container image from ECR.
- Create the ECS cluster by running this command:
aws ecs create-cluster --cluster-name ecs-bluegreen-cluster
What does this command do?
This creates a new ECS cluster named ecs-bluegreen-cluster. A Fargate cluster does not need EC2 instances. ECS manages the compute for you.
- Confirm the cluster is active. You should see "status": "ACTIVE" in the output.
Next, you need an IAM role that allows ECS to pull images from ECR on your behalf.
- Create a trust policy file for the ECS task execution role by running:
cat <<EOF > trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
- Create the IAM role by running:
aws iam create-role \
--role-name ecsTaskExecutionRole \
--assume-role-policy-document file://trust-policy.json
- Attach the managed policy that grants ECR and CloudWatch Logs permissions by running:
aws iam attach-role-policy \
--role-name ecsTaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
What is the ecsTaskExecutionRole?
This role lets ECS pull your Docker image from ECR and write logs to CloudWatch. Without it, ECS cannot start your container.
The trust policy allows ecs-tasks.amazonaws.com to assume the role. The attached AmazonECSTaskExecutionRolePolicy grants the actual permissions.
- Verify the role was created by running:
aws iam get-role --role-name ecsTaskExecutionRole --query "Role.Arn"
- You should see an ARN like arn:aws:iam::123456789012:role/ecsTaskExecutionRole. Note this ARN for the next substep.
Seeing "EntityAlreadyExists" error?
This means the role already exists in your account (perhaps from a previous project). That is fine. Skip the create-role step and just attach the policy. If the policy is already attached, you will see a message indicating it. You can proceed safely.
help me troubleshoot the ecsTaskExecutionRole creation error
Create and register the task definition
A task definition is a blueprint that tells ECS how to run your container. It specifies the image, CPU, memory, port mappings, and networking mode.
- In your ecs-blue-green-app repository folder, create a file named taskdef.json and paste the following content:
{
"executionRoleArn": "arn:aws:iam::[[AWS_ACCOUNT_ID=\"your 12-digit AWS account ID\"]]:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "ecs-bluegreen-container",
"image": "<IMAGE1_NAME>",
"essential": true,
"portMappings": [
{
"hostPort": 80,
"protocol": "tcp",
"containerPort": 80
}
]
}
],
"requiresCompatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"cpu": "256",
"memory": "512",
"family": "ecs-bluegreen-taskdef"
}
What does this task definition do?
- "family" names this task definition so ECS can track revisions.
- "image": "<IMAGE1_NAME>" is a placeholder that CodePipeline will replace with the actual ECR image URI during deployments. You will register the initial task definition with your real image URI in the next command.
- "cpu": "256" and "memory": "512" allocate 0.25 vCPU and 0.5 GB RAM (the smallest Fargate configuration).
- "networkMode": "awsvpc" gives each task its own elastic network interface, which is required for Fargate.
The taskdef.json file in your repo uses the <IMAGE1_NAME> placeholder for the pipeline. To launch the service for the first time, you need to register a task definition with your actual ECR image URI.
- Register the task definition with your real image by running:
aws ecs register-task-definition \
--family ecs-bluegreen-taskdef \
--network-mode awsvpc \
--requires-compatibilities FARGATE \
--cpu 256 \
--memory 512 \
--execution-role-arn arn:aws:iam::[[AWS_ACCOUNT_ID="your 12-digit AWS account ID"]]:role/ecsTaskExecutionRole \
--container-definitions '[{"name":"ecs-bluegreen-container","image":"[[ECR_URI="your ECR repository URI, e.g. 123456789012.dkr.ecr.us-east-1.amazonaws.com/ecs-blue-green-app"]]:latest","essential":true,"portMappings":[{"hostPort":80,"protocol":"tcp","containerPort":80}]}]'
Why register separately from the file?
The taskdef.json file in your repository uses <IMAGE1_NAME> because CodePipeline dynamically replaces that placeholder during automated deployments. For this initial registration, you provide the actual ECR image URI so ECS can pull and run your container right now.
- You should see a JSON response containing "taskDefinitionArn" with revision :1 at the end.
- Verify the registration by running:
aws ecs list-task-definitions --family-prefix ecs-bluegreen-taskdef
- You should see ecs-bluegreen-taskdef:1 listed in the output.
Seeing a ClientException or invalid parameter error?
Make sure you replaced the account ID placeholder with your actual 12-digit AWS account ID (no dashes). Also check that the ECR URI matches the repository you created earlier. You can find it by running aws ecr describe-repositories --repository-names ecs-blue-green-app.
help me fix my task definition registration error
Create the ECS service with blue/green deployment controller
The ECS service keeps your desired number of tasks running and connects them to the load balancer. Setting the deployment controller to CODE_DEPLOY tells ECS that CodeDeploy will manage all future deployments using the blue/green strategy.
Before creating the service, you need your subnet IDs and the ALB security group ID from the previous step.
- Retrieve your default VPC subnet IDs by running:
aws ec2 describe-subnets \
--filters "Name=default-for-az,Values=true" \
--query "Subnets[*].SubnetId" \
--output text
- Note two subnet IDs from the output. These are the public subnets in your default VPC.
- Retrieve the security group ID attached to your ALB by running:
aws elbv2 describe-load-balancers \
--names ecs-bluegreen-alb \
--query "LoadBalancers[0].SecurityGroups[0]" \
--output text
- Note the security group ID from the output (it looks like sg-0abc1234def56789).
- Retrieve the ARN of your first target group by running:
aws elbv2 describe-target-groups \
--names ecs-tg-1 \
--query "TargetGroups[0].TargetGroupArn" \
--output text
- Note the full target group ARN from the output.
- Create a file named service-definition.json with the following content, substituting your values:
{
"cluster": "ecs-bluegreen-cluster",
"serviceName": "ecs-bluegreen-service",
"taskDefinition": "ecs-bluegreen-taskdef:1",
"loadBalancers": [
{
"targetGroupArn": "[[TG_ARN=\"your ecs-tg-1 target group ARN\"]",
"containerName": "ecs-bluegreen-container",
"containerPort": 80
}
],
"launchType": "FARGATE",
"schedulingStrategy": "REPLICA",
"deploymentController": {
"type": "CODE_DEPLOY"
},
"networkConfiguration": {
"awsvpcConfiguration": {
"assignPublicIp": "ENABLED",
"securityGroups": ["[[SG_ID=\"your ALB security group ID\"]"],
"subnets": ["[[SUBNET_1=\"your first subnet ID\"]", "[[SUBNET_2=\"your second subnet ID\"]"]
}
},
"desiredCount": 1
}
What does this service configuration do?
- "deploymentController": {"type": "CODE_DEPLOY"} tells ECS that CodeDeploy will handle all future deployments using blue/green traffic shifting.
- "loadBalancers" connects your service to ecs-tg-1 so the ALB can route traffic to your container.
- "assignPublicIp": "ENABLED" gives each task a public IP so it can pull images from ECR (required for public subnets without a NAT gateway).
- "desiredCount": 1 keeps exactly one task running at all times.
- Create the ECS service by running:
aws ecs create-service \
--cli-input-json file://service-definition.json
- You should see a JSON response with "status": "ACTIVE" in the service object.
- Wait about 1-2 minutes for the task to start, then verify it is running:
aws ecs describe-services \
--cluster ecs-bluegreen-cluster \
--services ecs-bluegreen-service \
--query "services[0].{status:status,running:runningCount,desired:desiredCount}"
- You should see "running": 1 and "desired": 1. If running still shows 0, wait another minute and run the command again.
- Verify your application is accessible by opening your ALB DNS name in a browser. Get the DNS name by running:
aws elbv2 describe-load-balancers \
--names ecs-bluegreen-alb \
--query "LoadBalancers[0].DNSName" \
--output text
- Copy the DNS name and paste it into your browser address bar (use http://, not https).
- You should see your ECS Blue/Green Deployment page with "Version 1.0" and "Deployment Status: Live".
Service stuck at 0 running tasks?
Check the service events for error messages by running:
aws ecs describe-services \
--cluster ecs-bluegreen-cluster \
--services ecs-bluegreen-service \
--query "services[0].events[:3]"
Common causes include the security group not allowing traffic on port 80, subnets without internet access, or the execution role missing ECR permissions. Make sure you used the same security group and subnets that your ALB uses.
help me troubleshoot my ECS service not starting tasks
✔️ Awesome, I've got everything!
Great. Double check you have saved taskdef.json in your repository folder. You can delete trust-policy.json and service-definition.json as they are no longer needed.
ⓧ I'd like to double check the full code
Here is the taskdef.json file that should be in your repository:
{
"executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "ecs-bluegreen-container",
"image": "<IMAGE1_NAME>",
"essential": true,
"portMappings": [
{
"hostPort": 80,
"protocol": "tcp",
"containerPort": 80
}
]
}
],
"requiresCompatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"cpu": "256",
"memory": "512",
"family": "ecs-bluegreen-taskdef"
}
Your container is live and serving traffic through the ALB. Next up, you will configure CodeDeploy to manage blue/green deployments for this service.
Configure CodeDeploy for ECS Blue/Green
Your ECS service is running and accessible through the ALB. The next piece of the puzzle is CodeDeploy, which orchestrates the actual blue/green traffic shifting between your old and new container versions.
CodeDeploy needs its own IAM role, an application to organize deployments, a deployment group that ties everything together, and an AppSpec file that tells it which container to deploy.
In this step, get ready to:
- Create an IAM role and CodeDeploy application for ECS blue/green deployments.
- Create a deployment group linking your cluster, service, ALB, and target groups.
- Create the AppSpec file that CodeDeploy uses during deployment.
Create the CodeDeploy IAM role and application
CodeDeploy needs permission to update your ECS service, manage target groups, and shift traffic on your ALB. You create a dedicated IAM role with the AWSCodeDeployRoleForECS managed policy for this purpose.
- Create a trust policy file for CodeDeploy by running this command in your ecs-blue-green-app directory:
cat > codedeploy-trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "codedeploy.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
What does this trust policy do?
A trust policy defines which AWS service is allowed to assume this role. Here you grant codedeploy.amazonaws.com permission to use the role on your behalf.
- Create the IAM role named CodeDeployECSRole by running:
aws iam create-role \
--role-name CodeDeployECSRole \
--assume-role-policy-document file://codedeploy-trust-policy.json
- Attach the AWSCodeDeployRoleForECS managed policy to the role by running:
aws iam attach-role-policy \
--role-name CodeDeployECSRole \
--policy-arn arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS
- Verify the role was created by running:
aws iam get-role --role-name CodeDeployECSRole --query 'Role.Arn' --output text
You should see an ARN like arn:aws:iam::ACCOUNT_ID:role/CodeDeployECSRole. Copy this value for the next substep.
Seeing an EntityAlreadyExists error?
If the role already exists from a previous attempt, you can skip the create-role command and just run the attach-role-policy command to make sure the policy is attached.
help me fix my CodeDeployECSRole IAM role creation error
- Create the CodeDeploy application with the ECS compute platform by running:
aws deploy create-application \
--application-name ecs-bluegreen-app \
--compute-platform ECS
You should see output containing an applicationId value. This confirms your CodeDeploy application is ready.
What is a CodeDeploy application?
A CodeDeploy application is a container that groups deployment groups and revisions. Setting the compute platform to ECS tells CodeDeploy this application manages container deployments rather than EC2 instances or Lambda functions.
Create the CodeDeploy deployment group
The deployment group ties your CodeDeploy application to the specific ECS resources it will manage. It needs to know your cluster, service, ALB listeners, and target groups.
- First, get your ALB production listener ARN (port 80) by running:
aws elbv2 describe-listeners \
--load-balancer-arn $(aws elbv2 describe-load-balancers --names ecs-bluegreen-alb --query 'LoadBalancers[0].LoadBalancerArn' --output text) \
--query 'Listeners[?Port==`80`].ListenerArn' --output text
- Copy the listener ARN from the output. You will need it in the deployment group configuration.
- Get the test listener ARN (port 8080) by running:
aws elbv2 describe-listeners \
--load-balancer-arn $(aws elbv2 describe-load-balancers --names ecs-bluegreen-alb --query 'LoadBalancers[0].LoadBalancerArn' --output text) \
--query 'Listeners[?Port==`8080`].ListenerArn' --output text
- Copy this ARN as well.
- Create the deployment group configuration file by running the following command. Replace the two listener ARN placeholders with the values you just copied:
cat > deployment-group.json << 'EOF'
{
"applicationName": "ecs-bluegreen-app",
"deploymentGroupName": "ecs-bluegreen-dg",
"deploymentStyle": {
"deploymentOption": "WITH_TRAFFIC_CONTROL",
"deploymentType": "BLUE_GREEN"
},
"blueGreenDeploymentConfiguration": {
"deploymentReadyOption": {
"actionOnTimeout": "CONTINUE_DEPLOYMENT",
"waitTimeInMinutes": 0
},
"terminateBlueInstancesOnDeploymentSuccess": {
"action": "TERMINATE",
"terminationWaitTimeInMinutes": 5
}
},
"loadBalancerInfo": {
"targetGroupPairInfoList": [
{
"targetGroups": [
{ "name": "ecs-tg-1" },
{ "name": "ecs-tg-2" }
],
"prodTrafficRoute": {
"listenerArns": ["[[PROD_LISTENER_ARN="your production listener ARN for port 80"]]"]
},
"testTrafficRoute": {
"listenerArns": ["[[TEST_LISTENER_ARN="your test listener ARN for port 8080"]]"]
}
}
]
},
"ecsServices": [
{
"serviceName": "ecs-bluegreen-service",
"clusterName": "ecs-bluegreen-cluster"
}
],
"serviceRoleArn": "[[CODEDEPLOY_ROLE_ARN="arn:aws:iam::ACCOUNT_ID:role/CodeDeployECSRole"]]"
}
EOF
What is this configuration doing?
The deploymentStyle tells CodeDeploy to use blue/green with traffic control through your ALB.
The blueGreenDeploymentConfiguration sets actionOnTimeout to CONTINUE_DEPLOYMENT, which means traffic reroutes immediately without waiting for manual approval. After a successful deployment, the old (blue) tasks are terminated after 5 minutes.
The targetGroupPairInfoList links both target groups with the production listener (port 80) and test listener (port 8080), letting CodeDeploy route traffic between them during deployment.
- Create the deployment group by running:
aws deploy create-deployment-group \
--cli-input-json file://deployment-group.json \
--region us-east-1
You should see output containing a deploymentGroupId value confirming the deployment group was created.
Seeing a role error?
If you get an error about the service role, double-check that you replaced the serviceRoleArn placeholder with your full CodeDeployECSRole ARN (including your account ID).
If you see an InvalidECSServiceException, confirm your ECS service name is exactly ecs-bluegreen-service and the cluster is ecs-bluegreen-cluster.
help me troubleshoot my CodeDeploy deployment group creation error
Create the AppSpec file
CodeDeploy uses an AppSpec file to know which ECS task definition and container configuration to deploy. The file lives in your repository and gets picked up by the pipeline.
- Create the appspec.yaml file in your ecs-blue-green-app repository by running:
cat > appspec.yaml << 'EOF'
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <TASK_DEFINITION>
LoadBalancerInfo:
ContainerName: "ecs-bluegreen-container"
ContainerPort: 80
EOF
What are the placeholders in this file?
The <TASK_DEFINITION> placeholder is special. During deployment, CodeDeploy automatically replaces it with the actual task definition ARN that was registered. You never need to hard-code a specific revision number here.
The ContainerName must match exactly what you defined in taskdef.json (ecs-bluegreen-container). This is how CodeDeploy knows which container in the task definition receives the load balancer traffic.
- Verify the file was created correctly by running:
cat appspec.yaml
You should see the YAML content with <TASK_DEFINITION> as a placeholder and ecs-bluegreen-container as the container name.
- Confirm your repository now has all the expected files by running:
ls
You should see Dockerfile, index.html, taskdef.json, and appspec.yaml listed.
Missing a file?
Make sure you are in the ecs-blue-green-app directory. Run pwd to check. If you see a different directory, navigate back with cd ~/Desktop/ecs-blue-green-app.
help me find my missing project files
✔️ Awesome, I've got everything!
Great. Make sure you have saved all your files.
ⓧ I'd like to double check the full code
Here is what your appspec.yaml should look like:
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <TASK_DEFINITION>
LoadBalancerInfo:
ContainerName: "ecs-bluegreen-container"
ContainerPort: 80
And your taskdef.json should still have the <IMAGE1_NAME> placeholder in the image field:
{
"executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "ecs-bluegreen-container",
"image": "<IMAGE1_NAME>",
"essential": true,
"portMappings": [
{
"hostPort": 80,
"protocol": "tcp",
"containerPort": 80
}
]
}
],
"requiresCompatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"cpu": "256",
"memory": "512",
"family": "ecs-bluegreen-taskdef"
}
Your CodeDeploy configuration is complete. Next up, you will create the CodeBuild project that builds your Docker image and produces the deployment artifacts.
Create the CodeBuild Project
Your CodeDeploy configuration is ready to receive new container versions and shift traffic between your blue and green task sets.
Now you need something to actually build those container images. CodeBuild is a fully managed build service that compiles source code, runs tests, and produces artifacts. In this step, you will create a CodeBuild project that pulls your code from GitHub, builds a Docker image, pushes it to ECR, and outputs an imageDetail.json file that CodePipeline uses to trigger deployments.
In this step, get ready to:
- Create an IAM role for CodeBuild with ECR, CloudWatch Logs, and S3 permissions.
- Write a buildspec.yml that builds and pushes your Docker image.
- Create a CodeBuild project in the console connected to your GitHub repository.
Create the CodeBuild IAM role
CodeBuild needs permissions to interact with other AWS services on your behalf. You will create a dedicated service role that grants access to ECR (to push and pull images), CloudWatch Logs (to write build logs), and S3 (to read and write pipeline artifacts).
- Open the IAM console by searching for IAM in the AWS console search bar.
- In the left navigation pane, click **Roles**.
- Click **Create role**.
- Under **Trusted entity type**, select **AWS service**.
- Under **Use case**, select **CodeBuild** from the dropdown.
- Click **Next**.
- On the **Add permissions** page, do not attach any managed policies yet. Click **Next**.
- For **Role name**, enter CodeBuildECSRole.
- Click **Create role**.
Now you need to add an inline policy with the specific permissions CodeBuild needs for this project.
- In the **Roles** list, click **CodeBuildECSRole** to open it.
- Click the **Permissions** tab.
- Click **Add permissions**, then select **Create inline policy**.
- Click the **JSON** tab in the policy editor.
- Replace the existing content with the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CloudWatchLogsPolicy",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Sid": "S3ArtifactPolicy",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
],
"Resource": "*"
},
{
"Sid": "ECRPullPolicy",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "*"
},
{
"Sid": "ECRAuthPolicy",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Sid": "ECRPushPolicy",
"Effect": "Allow",
"Action": [
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": "*"
}
]
}
What does this policy do?
This policy grants CodeBuild the minimum permissions it needs for your pipeline workflow.
- CloudWatchLogsPolicy lets CodeBuild write build logs so you can debug failed builds.
- S3ArtifactPolicy lets CodeBuild read source artifacts from CodePipeline and write build output artifacts back.
- ECRPullPolicy and ECRAuthPolicy let CodeBuild authenticate and pull base images from ECR.
- ECRPushPolicy lets CodeBuild push your newly built Docker image to your ECR repository.
- Click **Next**.
- For **Policy name**, enter CodeBuildECSPolicy.
- Click **Create policy**.
You should see the policy listed under the **Permissions** tab for your CodeBuildECSRole role.
Don't see the policy attached?
Make sure you clicked into the correct role (CodeBuildECSRole) before adding the inline policy. If you created the policy under a different role, delete it and repeat the steps on the correct role.
Help me fix my CodeBuild IAM role permissions.
Create the buildspec.yml file
A buildspec file tells CodeBuild exactly what commands to run during each phase of the build. Your buildspec will log into ECR, build the Docker image, tag it, push it, and produce an imageDetail.json artifact that CodePipeline passes to the deploy stage.
- In your code editor, create a new file called buildspec.yml in the root of your ecs-blue-green-app repository.
- Paste the following content into buildspec.yml:
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- 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/ecs-blue-green-app
- IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION
build:
commands:
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:$IMAGE_TAG .
- docker tag $REPOSITORY_URI:$IMAGE_TAG $REPOSITORY_URI:latest
post_build:
commands:
- echo Pushing the Docker image to ECR...
- docker push $REPOSITORY_URI:$IMAGE_TAG
- docker push $REPOSITORY_URI:latest
- echo Writing imageDetail.json...
- printf '{"ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
files:
- imageDetail.json
What does this buildspec do?
The buildspec has three phases that run in sequence.
- pre_build authenticates Docker to your ECR registry and sets up the repository URI and image tag variables. The $CODEBUILD_RESOLVED_SOURCE_VERSION variable is the Git commit hash, which gives each image a unique tag.
- build runs docker build to create your image and tags it with both the commit hash and latest.
- post_build pushes both tags to ECR, then writes imageDetail.json with the full image URI. CodePipeline reads this file to know which image to deploy.
- Save buildspec.yml.
✔️ Awesome, I've got everything!
Great. Double check you have saved the file in the root of your ecs-blue-green-app repository (same level as your Dockerfile).
ⓧ I'd like to double check the full code
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- 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/ecs-blue-green-app
- IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION
build:
commands:
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:$IMAGE_TAG .
- docker tag $REPOSITORY_URI:$IMAGE_TAG $REPOSITORY_URI:latest
post_build:
commands:
- echo Pushing the Docker image to ECR...
- docker push $REPOSITORY_URI:$IMAGE_TAG
- docker push $REPOSITORY_URI:latest
- echo Writing imageDetail.json...
- printf '{"ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
files:
- imageDetail.json
YAML indentation error?
YAML files are sensitive to indentation. Make sure you use spaces (not tabs) and that each level is indented by exactly two spaces. If you copied and pasted, check that no extra whitespace was introduced.
Help me fix YAML formatting issues in my buildspec.
Create the CodeBuild project in the console
With your IAM role and buildspec ready, you can now create the CodeBuild project that ties everything together.
- Open the CodeBuild console by searching for CodeBuild in the AWS console search bar.
- In the left navigation pane, expand **Build** and click **Build projects**.
- Click **Create build project**.
The create project wizard has several sections. Fill each one as described below.
- In the **Project configuration** section, enter ecs-bluegreen-build for **Project name**.
- In the **Source** section, select **GitHub** for **Source provider**.
- If this is your first time connecting GitHub to CodeBuild, click **Connect to GitHub** and authorize AWS CodeBuild to access your account.
- Select **Repository in my GitHub account**.
- Search for and select your ecs-blue-green-app repository.
Why connect GitHub directly?
CodeBuild uses OAuth or a GitHub App connection to access your repository. This is separate from the CodePipeline connection you will set up later. When CodePipeline triggers CodeBuild, it passes the source code as an input artifact, so CodeBuild does not need to pull from GitHub directly during pipeline runs.
Configuring the source here ensures CodeBuild can also run standalone builds if needed.
- In the **Environment** section, select **Managed image**.
- For **Operating system**, select **Amazon Linux**.
- For **Runtime(s)**, select **Standard**.
- For **Image**, select aws/codebuild/amazonlinux2-x86_64-standard:5.0.
- Check the **Privileged** checkbox. This is required for building Docker images inside CodeBuild.
- For **Service role**, select **Existing service role**.
- In the **Role ARN** dropdown, select CodeBuildECSRole.
- Uncheck **Allow AWS CodeBuild to modify this service role so it can be used with this build project** (you already configured the permissions manually).
Why enable privileged mode?
Docker runs as a daemon process that requires elevated system access. Without privileged mode, the docker build and docker push commands inside your buildspec would fail with permission errors.
- Expand **Additional configuration** under the Environment section.
- Scroll down to **Environment variables** and add two variables:
- Name: AWS_ACCOUNT_ID with a Value of your 12-digit AWS account ID (find it in the top-right corner of the console).
- Name: AWS_DEFAULT_REGION with a Value of us-east-1.
- In the **Buildspec** section, select **Use a buildspec file**. Leave the **Buildspec name** field empty (it defaults to buildspec.yml in the source root).
- In the **Artifacts** section, select **No artifacts**. CodePipeline will handle artifact passing between stages.
- Leave the **Logs** section at its default (CloudWatch Logs enabled).
- Click **Create build project**.
You should see a success message and your ecs-bluegreen-build project listed in the Build projects view.
Seeing an access denied error?
If you get an error when selecting the CodeBuildECSRole role, confirm that the trust policy for the role includes codebuild.amazonaws.com as the trusted service. You set this when you selected CodeBuild as the use case during role creation.
Help me fix my CodeBuild project creation error.
Your build infrastructure is complete. Next, you will wire everything together into a CodePipeline V2 pipeline that automatically triggers when you push code to GitHub.
Create the CodePipeline V2 Pipeline
You have all the individual services built and configured. Now it is time to connect them into a single automated workflow.
CodePipeline is the orchestrator that ties your GitHub repository, CodeBuild project, ECR repository, and CodeDeploy deployment group into one continuous delivery pipeline. When code changes land on your main branch, CodePipeline detects the change and automatically triggers the entire build-and-deploy sequence.
In this step, get ready to:
- Create a CodePipeline V2 pipeline with a GitHub source action and an ECR source action.
- Add a Build stage that uses your CodeBuild project.
- Add a Deploy stage that uses the Amazon ECS (Blue/Green) provider with CodeDeploy.
Create the pipeline and add the GitHub source
The pipeline creation wizard walks you through each stage one at a time. You will start with your GitHub repository as the primary source, then come back and add the ECR source as a parallel action.
- Open the AWS Management Console and navigate to the CodePipeline service.
- Click Create pipeline.
- On the Choose creation option page, select Build custom pipeline.
- Click Next.
- In Pipeline name, enter ecs-bluegreen-pipeline.
- The Pipeline type defaults to V2. Leave it as-is.
- In Service role, select New service role.
- Leave the Advanced settings at their defaults.
- Click Next.
Why V2?
V2 is the only pipeline type available in the console. It supports trigger filters, parallel execution modes, and is priced at $0.002 per action execution minute with 100 free minutes per month.
- On the Add source stage page, in Source provider, choose GitHub (via GitHub App).
- Under Connection, click Connect to GitHub to create a new connection.
What is a CodeConnection?
CodeConnections (formerly CodeStar Connections) is how AWS securely connects to third-party source providers like GitHub. When you create a connection, AWS installs a GitHub App on your account that grants CodePipeline read access to your repositories.
- A pop-up window appears. Enter a connection name such as github-connection and click Connect to GitHub.
- You will be redirected to GitHub to authorize the AWS Connector app. Click Authorize to grant access.
- Back in the AWS console, click Install a new app if prompted, then select your GitHub account and grant access to the ecs-blue-green-app repository.
- Click Connect to finalize the connection.
GitHub authorization not working?
Make sure you are signed into the correct GitHub account in your browser. If you belong to a GitHub organization, only the organization owner can authorize the app. For personal repositories, you must be the repository owner.
Help me troubleshoot my GitHub connection in CodePipeline.
- In Repository name, select ecs-blue-green-app.
- In Default branch, select main.
- Leave the Output artifact format as CodePipeline default.
- Click Next.
Add the Build stage
The Build stage tells CodePipeline to use your CodeBuild project to build the Docker image and produce the imageDetail.json artifact.
- On the Add build stage page, in Build provider, choose AWS CodeBuild.
- In Project name, select ecs-bluegreen-build.
- Leave Build type as Single build.
- Click Next.
What about the input and output artifacts?
CodePipeline automatically wires the source stage output (SourceArtifact) as the input to the Build stage. The Build stage output is named BuildArtifact by default and will contain the imageDetail.json file your buildspec produces.
- On the Add test stage page, click Skip test stage and confirm by clicking Skip.
- Click Next.
Add the Deploy stage and wire up the artifacts
The Deploy stage uses the Amazon ECS (Blue/Green) provider. This tells CodePipeline to hand off to CodeDeploy, which performs the blue/green traffic shift on your ECS service.
- On the Add deploy stage page, in Deploy provider, choose Amazon ECS (Blue/Green).
- In Application name, select ecs-bluegreen-app.
- In Deployment group, select ecs-bluegreen-dg.
- Under Amazon ECS task definition, choose SourceArtifact from the dropdown. In the file path field, enter taskdef.json.
- Under AWS CodeDeploy AppSpec file, choose SourceArtifact from the dropdown. In the file path field, enter appspec.yaml.
- Leave the Dynamically update task definition image section empty for now. You will configure this after adding the ECR source action.
- Click Next.
- On the Review page, review your pipeline configuration.
- Click Create pipeline.
The pipeline will attempt to run immediately. It may fail at the Deploy stage because the ECR source artifact is not wired yet. That is expected.
Why does the pipeline start automatically?
CodePipeline runs automatically after creation to verify the configuration. Since you have not yet added the ECR source action or wired the image artifact to the Deploy stage, this initial run will fail. You will fix this in the next substep.
Add the ECR source action and wire the image artifact
Your pipeline needs a second source action that monitors your ECR repository. When a new image is pushed, this action produces an imageDetail.json file containing the image URI. The Deploy stage uses this to dynamically replace the <IMAGE1_NAME> placeholder in your task definition.
- In the CodePipeline console, click Stop execution if the pipeline is currently running, then choose Stop and wait.
- Click Edit in the upper left corner of your pipeline.
- In the Source stage, click Edit stage.
- Click + Add action next to your existing GitHub source action to add a parallel action.
- In Action name, enter ECRSource.
- In Action provider, choose Amazon ECR.
- In Repository name, select ecs-blue-green-app.
- Leave Image tag as latest.
- In Output artifacts, change the default name to ECRSourceArtifact.
- Click Done on the action pane.
- Click Done on the stage pane.
Now wire the ECR artifact to the Deploy stage so CodeDeploy knows which image to deploy.
- In the Deploy stage, click Edit stage.
- Click the edit icon on the Deploy action.
- Scroll down to Input artifacts and click Add. Select ECRSourceArtifact from the dropdown.
- Verify that Amazon ECS task definition is set to SourceArtifact with taskdef.json.
- Verify that AWS CodeDeploy AppSpec file is set to SourceArtifact with appspec.yaml.
- Under Dynamically update task definition image, in Input artifact with image URI, select ECRSourceArtifact.
- In the Placeholder text in the task definition field, enter IMAGE1_NAME.
- Click Done on the action pane.
- Click Done on the stage pane.
What does IMAGE1_NAME do?
When the Deploy action runs, CodePipeline reads the imageDetail.json file from ECRSourceArtifact (or BuildArtifact). It finds the image URI inside, then replaces the <IMAGE1_NAME> placeholder in your taskdef.json with the actual URI. This is how your pipeline always deploys the correct image version.
- Click Save at the top of the pipeline editor.
- In the confirmation dialog, click Save.
Your pipeline now has all three stages fully configured. The Source stage has two parallel actions (GitHub and ECR), the Build stage uses CodeBuild, and the Deploy stage uses CodeDeploy with your ECS blue/green configuration.
Don't see ECRSourceArtifact in the Deploy input artifacts dropdown?
Make sure you saved the Source stage edits before editing the Deploy stage. The artifact name must match exactly what you entered in the ECR action's output artifact field.
Help me fix my Deploy stage artifact configuration in CodePipeline.
Your pipeline is saved and ready to run. In the next step, you will trigger it by pushing code and watch the full blue/green deployment in action.
Trigger the Pipeline and Verify Blue/Green Deployment
Your entire pipeline infrastructure is in place. Every piece is configured and waiting for a trigger. The moment you push code to GitHub, CodePipeline detects the change, builds your image, and kicks off a blue/green deployment.
In this step, you will trigger that pipeline for real and watch traffic shift from the old (blue) version to the new (green) version with zero downtime.
In this step, get ready to:
- Push your repository files to GitHub and trigger the pipeline automatically.
- Monitor the blue/green deployment stages in CodeDeploy.
- Verify zero-downtime deployment by pushing a second code change end-to-end.
Push your files to GitHub and watch the pipeline trigger
Your local repository has five files that the pipeline needs: Dockerfile, index.html, buildspec.yml, appspec.yaml, and taskdef.json. Pushing them to the main branch is what triggers the Source stage in your pipeline.
- Open your terminal and navigate to your ecs-blue-green-app folder on your Desktop.
- Stage, commit, and push all files to GitHub by running these commands:
git add .
git commit -m "Initial pipeline files"
git push origin main
What does this trigger?
Your pipeline's Source stage has a GitHub connection that watches the main branch. When GitHub receives your push, CodePipeline V2 detects the change and starts the pipeline automatically.
- Open the AWS Management Console and navigate to the CodePipeline service.
- Click on ecs-bluegreen-pipeline to view its details.
You should see the Source stage transition to In Progress or Succeeded within a minute or two.
Pipeline not triggering?
Make sure you pushed to the main branch (not master or another branch). The pipeline's Source action is configured to watch main specifically.
Also confirm that the GitHub connection in CodePipeline settings shows as Available. If it says Pending, you need to complete the OAuth handshake in the Connections page.
Help me troubleshoot why my CodePipeline is not triggering after a git push to main
- Wait for the Build stage to show In Progress. This means CodeBuild is building your Docker image.
- Click the Details link on the Build action to watch the build logs in real time.
The build takes about 2-3 minutes. You should see log lines for the ECR login, Docker build, Docker push, and the imageDetail.json file being written.
Build stage failing?
Check the build logs for the specific error. Common issues include: the CodeBuild service role missing ECR permissions, privileged mode not being enabled (required for Docker builds), or environment variables AWS_ACCOUNT_ID and AWS_DEFAULT_REGION not being set in the CodeBuild project.
Help me debug my CodeBuild project that fails when building a Docker image for ECR
Monitor the blue/green deployment in CodeDeploy
Once the Build stage succeeds, CodePipeline hands off to the Deploy stage. This is where CodeDeploy performs the blue/green deployment on your ECS service.
- In the AWS Console, navigate to the CodeDeploy service.
- Click Deployments in the left sidebar.
- Click on the active deployment to view its details.
You should see the deployment progressing through its stages. The key thing to observe is how the original (blue) task set stays running while the replacement (green) task set is provisioned.
What is happening during blue/green?
CodeDeploy provisions a new set of ECS tasks (the green set) using your updated task definition and the new image from ECR. It registers these green tasks with ecs-tg-2 (your second target group).
The test listener on port 8080 routes to ecs-tg-2, so you can verify the green deployment before any production traffic reaches it. Meanwhile, the blue tasks continue serving production traffic on port 80 through ecs-tg-1.
- Wait for the deployment status to show that the replacement task set is provisioned and registered with the target group.
- Open a browser tab and navigate to your ALB DNS name on port 8080 to verify the green deployment via the test listener:
http://[[ALB_DNS="your-alb-dns-name.us-east-1.elb.amazonaws.com"]]:8080
You should see your application page with the heading "ECS Blue/Green Deployment" and "Version 1.0" displayed in green text.
Where do I find my ALB DNS name?
In the EC2 console, click Load Balancers in the left sidebar. Select ecs-bluegreen-alb and find the DNS name in the Description tab. It looks something like ecs-bluegreen-alb-123456789.us-east-1.elb.amazonaws.com.
- Go back to the CodeDeploy deployment details page.
- Watch for the traffic rerouting step to complete. Since you configured the deployment group to reroute traffic immediately, CodeDeploy shifts production traffic from the blue target group to the green target group without manual intervention.
Once the deployment shows Succeeded, all production traffic on port 80 now routes to your green task set. The old blue tasks are terminated.
Deployment stuck or failing?
If the deployment is stuck at "Install", check that the ECS service's security group allows inbound traffic from the ALB security group on port 80. The green tasks need the ALB to reach them for health checks.
If the deployment fails with a task definition error, verify that your taskdef.json in GitHub has <IMAGE1_NAME> as the image value and that your appspec.yaml uses the container name ecs-bluegreen-container (matching your task definition).
Help me troubleshoot my ECS blue/green deployment that is failing in CodeDeploy
Verify production and push a second change
The real proof of zero-downtime deployment is pushing a visible change and watching the whole pipeline run again without any interruption to production traffic.
- Open a browser tab and navigate to your ALB DNS name on port 80 (the production listener):
http://[[ALB_DNS="your-alb-dns-name.us-east-1.elb.amazonaws.com"]]
You should see your application page with "ECS Blue/Green Deployment" as the heading and "Version 1.0" in green text. This confirms the green deployment is now serving production traffic.
- Open index.html in your local repository.
- Find this line:
<div class="version">Version 1.0</div>
- Change Version 1.0 to Version 2.0 so the line looks like this:
<div class="version">Version 2.0</div>
- Save index.html.
- Commit and push the change by running these commands:
git add index.html
git commit -m "Update to Version 2.0"
git push origin main
- Go back to the CodePipeline console and watch the pipeline trigger again.
- While the pipeline runs, refresh the production URL on port 80 periodically. Notice that it continues to serve the old version without any downtime until the new deployment completes.
- Wait for the entire pipeline (Source, Build, Deploy) to succeed.
- Refresh the production URL on port 80 after the deploy completes.
You should now see "Version 2.0" displayed in green text. The transition happened without any interruption to users visiting the production URL.
Why is this zero-downtime?
During the deployment, the old version kept serving on port 80. CodeDeploy only rerouted production traffic after the new tasks passed health checks and were fully ready. At no point did users see an error page or a connection timeout.
Still seeing Version 1.0 after deploy completes?
Try a hard refresh (Cmd+Shift+R on macOS or Ctrl+Shift+R on Windows) to bypass browser caching. If you still see the old version, confirm the deployment in CodeDeploy actually shows Succeeded and check that you are hitting port 80 (not port 8080).
Help me figure out why my ECS deployment shows the old version after CodeDeploy succeeded
You have just proven that your pipeline delivers code from commit to production with zero-downtime blue/green deployments. Next up, you will take on a secret mission to add automated testing as a quality gate before deployment.
Secret mission
Add Automated Testing Before Deployment
Your pipeline deploys every build that passes CodeBuild. But what if a broken container image makes it through? Add a Test stage that runs automated health checks on your container before it ever reaches production.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses AWS resources that incur hourly charges while running.
Cost warning
Your Application Load Balancer costs ~$0.02/hour and your Fargate task costs ~$0.01/hour. Together, that is roughly $0.50-$1.00/day if left running.
If your AWS account is less than 12 months old, the ALB is covered under the free tier (750 hours/month). Fargate has no free tier. If you are done with the project, delete everything to avoid unexpected charges.
Resources you used:
- Application Load Balancer (ecs-bluegreen-alb) with two listeners (port 80, port 8080).
- Two target groups (ecs-tg-1 and ecs-tg-2).
- ECS Fargate cluster (ecs-bluegreen-cluster) and service.
- Amazon ECR repository (ecs-blue-green-app).
- CodePipeline V2 pipeline (ecs-bluegreen-pipeline).
- CodeBuild project.
- CodeDeploy application and deployment group.
- IAM roles (ecsTaskExecutionRole, CodeDeployECSRole, CodeBuild service role).
- GitHub repository (ecs-blue-green-app) and CodeConnections connection.
Keep everything running
No action needed. Choose this if you are still actively building or want to experiment with additional deployments.
- Leave all resources as-is.
- Expect roughly $0.50-$1.00/day in charges while the ALB and Fargate task run.
- CodePipeline V2 charges $0.002 per action execution minute, with 100 free minutes/month.
- CodeBuild provides 100 free build minutes/month on general1.small.
Pause - I'll come back to this later
Shut down your running containers to stop Fargate charges, but keep everything else in place so you can resume later.
- Scale your ECS service to zero tasks by running:
aws ecs update-service --cluster ecs-bluegreen-cluster --service my-service --desired-count 0
- This stops the Fargate task from running, eliminating the ~$0.01/hour Fargate cost.
- The ALB still costs ~$0.02/hour while it exists (free-tier eligible for accounts under 12 months old).
- To resume later, run the same command with --desired-count 1.
Delete - I don't want to use this again
Remove all project resources and start fresh. This is recommended after you have completed the project and do not need it running anymore.
- Delete the CodePipeline pipeline from the CodePipeline console.
- Delete the CodeDeploy application (this deletes the deployment group too).
- Delete the ECS service first, then delete the ECS cluster.
- Delete the Application Load Balancer and both target groups from the EC2 console.
- Delete the ECR repository by running:
aws ecr delete-repository --repository-name ecs-blue-green-app --force
- Delete the CodeBuild project from the CodeBuild console.
- Delete the IAM roles you created (CodeDeployECSRole, ecsTaskExecutionRole, and your CodeBuild service role).
- Remove the GitHub connection in the CodePipeline settings.
- Optionally delete the ecs-blue-green-app GitHub repository if you no longer need the source code.
That's a Wrap!
That's a Wrap!
Nice work! You just built a production-grade container CI/CD pipeline on AWS that automatically builds, tests, and deploys containerized applications with zero-downtime blue/green deployments.
You've learned how to:
- Built and pushed a custom Docker image to Amazon ECR, then deployed it on ECS Fargate behind an Application Load Balancer with two target groups.
- Configured CodeDeploy for ECS blue/green deployments with automatic traffic shifting and zero-downtime releases.
- Assembled a CodePipeline V2 pipeline connecting GitHub, CodeBuild, ECR, and CodeDeploy into a fully automated end-to-end deployment flow.
- Secret Mission: Added automated container health check testing as a quality gate before deployment.
Ready to quiz yourself?