If you’re building containerized apps and want zero-downtime deployments, GitLab CI/CD with Amazon ECS Fargate and Blue/Green deployments is a powerful combo. In this step-by-step guide, we’ll show you how to deploy a Docker app using GitLab pipelines, Amazon CodeDeploy, and ECS Fargate — with smooth traffic shifting and health-based validation.
- Why ECS Fargate with GitLab?
- Project Overview
- Step 1: Project Structure
- Step 2: Dockerize the App
- Step 3: Task Definition (taskdef.json)
- Step 4: GitLab CI/CD Pipeline
- Step 5: GitLab CI/CD Environment Variables
- Step 6: How Blue/Green Works with Fargate
- Conclusion
Why ECS Fargate with GitLab?
- No server management – Fargate is fully managed.
- GitLab CI/CD automates your build and deployment pipeline.
- Blue/Green deployment ensures zero-downtime updates with rollback safety.
Project Overview
We’ll create a simple Flask app, containerize it, push it to Amazon ECR, and use GitLab to deploy it to ECS Fargate with CodeDeploy Blue/Green.

What you’ll get:
- Docker app with
/healthcheck .gitlab-ci.ymlpipeline- Blue/Green deployment with ALB test listener
- Automatic traffic shifting after health validation
Step 1: Project Structure
Your project might look like this:
ecs-fargate-app/
├── Dockerfile
├── app.py
├── taskdef.json
├── .gitlab-ci.yml
Step 2: Dockerize the App
Dockerfile
FROM python:3.9-slim
COPY app.py .
RUN pip install flask
CMD ["python", "app.py"]
app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hello from ECS!"
@app.route('/health')
def health():
return "OK", 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
Step 3: Task Definition (taskdef.json)
Update the image placeholder later in the GitLab pipeline.
{
"family": "my-task",
"containerDefinitions": [
{
"name": "my-container",
"image": "<IMAGE_URI>",
"portMappings": [
{
"containerPort": 80,
"protocol": "tcp"
}
],
"essential": true
}
]
}
Step 4: GitLab CI/CD Pipeline
Create .gitlab-ci.yml in your root directory:
stages:
- build
- deploy
- validate
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
AWS_DEFAULT_REGION: us-east-1
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
ECR_URI: $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$ECR_REPO
before_script:
- apk add --no-cache curl bash python3 py3-pip jq
- pip3 install awscli
- aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URI
build:
stage: build
script:
- docker build -t $ECR_REPO:$IMAGE_TAG .
- docker tag $ECR_REPO:$IMAGE_TAG $ECR_URI:$IMAGE_TAG
- docker push $ECR_URI:$IMAGE_TAG
deploy:
stage: deploy
script:
- |
sed "s|<IMAGE_URI>|$ECR_URI:$IMAGE_TAG|g" taskdef.json > rendered-taskdef.json
- |
TASK_DEF_ARN=$(aws ecs register-task-definition --cli-input-json file://rendered-taskdef.json | jq -r '.taskDefinition.taskDefinitionArn')
- |
cat <<EOF > appspec.json
{
"version": 1,
"Resources": [
{
"TargetService": {
"Type": "AWS::ECS::Service",
"Properties": {
"TaskDefinition": "$TASK_DEF_ARN",
"LoadBalancerInfo": {
"ContainerName": "my-container",
"ContainerPort": 80
}
}
}
}
]
}
EOF
- |
APPSPEC_CONTENT=$(jq -Rs . < appspec.json)
aws deploy create-deployment \
--application-name "$CODEDEPLOY_APP" \
--deployment-group-name "$CODEDEPLOY_GROUP" \
--revision revisionType=AppSpecContent,appSpecContent="{\"content\": $APPSPEC_CONTENT, \"sha256\": \"\"}"
validate:
stage: validate
script:
- |
echo "Validating health endpoint on test listener..."
for i in {1..10}; do
curl -f http://$TEST_LB_DNS/health && break || sleep 5
done
echo "App passed health check"
Step 5: GitLab CI/CD Environment Variables
Go to your GitLab project ➝ Settings ➝ CI/CD ➝ Variables and add:
| Key | Description |
|---|---|
AWS_ACCESS_KEY_ID | Your IAM access key |
AWS_SECRET_ACCESS_KEY | Your IAM secret key |
AWS_ACCOUNT_ID | Your AWS account number |
ECR_REPO | Your ECR repo name (e.g., ecs-app) |
CODEDEPLOY_APP | CodeDeploy Application Name |
CODEDEPLOY_GROUP | Deployment Group Name |
TEST_LB_DNS | ALB test listener DNS name |
Step 6: How Blue/Green Works with Fargate
- GitLab pipeline pushes a new Docker image to ECR
- Registers a new ECS Task Definition
- Creates a new deployment via CodeDeploy
- ECS launches new task behind the test target group
- ALB health checks the new version via
/health - If healthy, CodeDeploy shifts traffic to green version

No downtime. No stress.
Conclusion
By combining GitLab CI/CD with ECS Fargate and Blue/Green deployments, you get a modern, scalable, and safe deployment pipeline. Even though Fargate limits lifecycle hooks, its simplicity and scalability make it perfect for most workloads.