What is AWS CodeDeploy?
AWS CodeDeploy is a deployment service that automates application deployments to EC2 instances, on-premises servers, Lambda functions, or ECS services.
Key Concepts
Application
A name that uniquely identifies the application you want to deploy. Container for deployment groups and revisions.
Deployment Group
Set of instances or Lambda functions where new revision is deployed. Includes deployment configuration and optional load balancer.
Deployment Configuration
Rules controlling deployment speed and pattern (e.g., deploy to 50% of instances at a time).
Revision
Version of deployable content (application files + appspec.yml). Stored in S3 or GitHub.
appspec.yml
Configuration file defining deployment actions and lifecycle event hooks.
Compute Platforms
1. EC2/On-Premises
Deploy applications to EC2 instances or on-premises servers.
2. Lambda
Deploy new versions of Lambda functions with traffic shifting.
3. ECS
Deploy containerized applications to ECS services.
Deployment Types
In-Place Deployment (EC2/On-Premises)
Application stopped on each instance, new revision installed, application restarted.
Process:
- Stop application on instance
- Install new version
- Start application
- Move to next instance
Pros:
- No additional infrastructure needed
- Simple and fast
Cons:
- Brief downtime per instance
- Cannot quickly rollback
Blue/Green Deployment
New instances (green) created with new version, traffic shifted from old instances (blue).
Process:
- Create new instances (green environment)
- Deploy application to green instances
- Register green instances with load balancer
- Shift traffic from blue to green
- Optionally terminate blue instances
Pros:
- Zero downtime
- Easy rollback (switch traffic back to blue)
- Test new version before full deployment
Cons:
- Requires double infrastructure temporarily
- More complex setup
Deployment Configurations
EC2/On-Premises
CodeDeployDefault.OneAtATime:
- Deploy to one instance at a time
- Slowest, safest
CodeDeployDefault.HalfAtATime:
- Deploy to 50% of instances simultaneously
- Balanced speed/safety
CodeDeployDefault.AllAtOnce:
- Deploy to all instances simultaneously
- Fastest, highest risk
Lambda
CodeDeployDefault.LambdaCanary10Percent5Minutes:
- 10% of traffic to new version
- Wait 5 minutes
- Shift remaining 90%
CodeDeployDefault.LambdaLinear10PercentEvery1Minute:
- Shift 10% of traffic every 1 minute
- Takes 10 minutes for full deployment
CodeDeployDefault.LambdaAllAtOnce:
- Shift 100% of traffic immediately
ECS
CodeDeployDefault.ECSCanary10Percent5Minutes:
- 10% of tasks to new version
- Wait 5 minutes
- Shift remaining 90%
CodeDeployDefault.ECSLinear10PercentEvery1Minute:
- Shift 10% every minute
CodeDeployDefault.ECSAllAtOnce:
- All tasks updated immediately
Custom Configuration
aws deploy create-deployment-config \
--deployment-config-name MyCustomConfig \
--minimum-healthy-hosts type=FLEET_PERCENT,value=75
minimum-healthy-hosts:
FLEET_PERCENT- Percentage that must stay healthy (e.g., 75%)HOST_COUNT- Absolute number that must stay healthy (e.g., 3 instances)
Lifecycle Events
EC2/On-Premises In-Place Deployment
Order of execution:
- ApplicationStop - Stop running application
- DownloadBundle - CodeDeploy downloads application revision (managed by CodeDeploy)
- BeforeInstall - Pre-installation tasks (backup, decrypt files)
- Install - Copy application files to destination (managed by CodeDeploy)
- AfterInstall - Post-installation tasks (set permissions, install dependencies)
- ApplicationStart - Start application services
- ValidateService - Verify application is running correctly
Blue/Green Deployment Additional Events
- BeforeBlockTraffic - Prepare for traffic removal
- BlockTraffic - Remove instance from load balancer (managed by CodeDeploy)
- AfterBlockTraffic - Verify no traffic reaching instance
- BeforeAllowTraffic - Final checks before receiving traffic
- AllowTraffic - Add instance to load balancer (managed by CodeDeploy)
- AfterAllowTraffic - Verify instance handling traffic correctly
Lambda Deployment Events
- BeforeAllowTraffic - Run tests against new version
- AllowTraffic - Shift traffic to new version (managed by CodeDeploy)
- AfterAllowTraffic - Validate new version
appspec.yml Structure
EC2/On-Premises
version: 0.0
os: linux
files:
- source: /
destination: /var/www/html
hooks:
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 300
BeforeInstall:
- location: scripts/backup_config.sh
timeout: 300
AfterInstall:
- location: scripts/install_dependencies.sh
timeout: 600
ApplicationStart:
- location: scripts/start_server.sh
timeout: 300
ValidateService:
- location: scripts/validate.sh
timeout: 300
Lambda
version: 0.0
Resources:
- MyFunction:
Type: AWS::Lambda::Function
Properties:
Name: "my-function"
Alias: "live"
CurrentVersion: "1"
TargetVersion: "2"
Hooks:
- BeforeAllowTraffic: "BeforeAllowTrafficHookFunctionName"
- AfterAllowTraffic: "AfterAllowTrafficHookFunctionName"
ECS
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "arn:aws:ecs:us-east-1:111111111111:task-definition/my-task:2"
LoadBalancerInfo:
ContainerName: "my-container"
ContainerPort: 80
Hooks:
- BeforeAllowTraffic: "BeforeAllowTrafficHookFunctionName"
- AfterAllowTraffic: "AfterAllowTrafficHookFunctionName"
How Deployment Starts
Method 1: Manual Trigger
Via Console:
- Click “Create deployment” in CodeDeploy console
- Select application revision location
Via CLI:
aws deploy create-deployment \
--application-name MyApp \
--deployment-group-name MyDeploymentGroup \
--s3-location bucket=my-bucket,key=my-app.zip,bundleType=zip
Method 2: Automated via CodePipeline
Pipeline Deploy stage triggers CodeDeploy:
{
"name": "Deploy",
"actions": [{
"name": "DeployToEC2",
"actionTypeId": {
"category": "Deploy",
"provider": "CodeDeploy"
},
"configuration": {
"ApplicationName": "MyApp",
"DeploymentGroupName": "Production"
}
}]
}
Method 3: Auto-Deploy on Push
GitHub/Bitbucket webhook triggers deployment:
aws deploy create-deployment \
--application-name MyApp \
--deployment-group-name MyDeploymentGroup \
--github-location repository=myuser/myrepo,commitId=abc123
DownloadBundle Event
What it does: CodeDeploy agent downloads application revision from source to EC2 instance.
Technical flow:
1. CodeDeploy service initiates deployment
2. Sends deployment instructions to CodeDeploy agent on EC2
3. DownloadBundle event starts
4. Agent downloads revision from S3/GitHub
5. Agent extracts files to temporary location
6. DownloadBundle completes
7. Next lifecycle event (BeforeInstall) starts
Source locations:
- S3 bucket:
s3://my-bucket/my-app-v1.zip - GitHub: Repository + commit ID
- Bitbucket: Repository + commit ID
Note: DownloadBundle is fully managed by CodeDeploy agent - you cannot run custom scripts during this event.
Blue/Green Instance Termination Options
Controls what happens to original (blue) instances after successful deployment.
1. Terminate Immediately
What happens:
- Green instances receive traffic
- Blue instances terminated immediately
Use case: Cost savings, most common choice
2. Keep Running
What happens:
- Green instances receive traffic
- Blue instances stay running (no traffic)
Use case: Quick rollback capability, testing period
3. Terminate After Waiting Period
What happens:
- Green instances receive traffic
- Blue instances wait X hours
- After waiting period, blue instances terminated
Use case: Safety buffer for rollback, automated cleanup
Example: Web Application Deployment
1. Create Application Bundle
# Application structure
my-app/
├── appspec.yml
├── index.html
├── app.js
└── scripts/
├── stop_server.sh
├── start_server.sh
└── validate.sh
# Zip and upload
zip -r my-app-v1.zip my-app/
aws s3 cp my-app-v1.zip s3://my-deployments-bucket/
2. Create Application
aws deploy create-application \
--application-name MyWebApp \
--compute-platform Server
3. Create Deployment Group
aws deploy create-deployment-group \
--application-name MyWebApp \
--deployment-group-name Production \
--deployment-config-name CodeDeployDefault.OneAtATime \
--ec2-tag-filters Key=Environment,Value=Production,Type=KEY_AND_VALUE \
--service-role-arn arn:aws:iam::111111111111:role/CodeDeployServiceRole
4. Deploy
aws deploy create-deployment \
--application-name MyWebApp \
--deployment-group-name Production \
--s3-location bucket=my-deployments-bucket,key=my-app-v1.zip,bundleType=zip
5. Monitor Deployment
# Get deployment status
aws deploy get-deployment --deployment-id d-ABC123
# List deployments
aws deploy list-deployments --application-name MyWebApp
# Get deployment details
aws deploy get-deployment-target \
--deployment-id d-ABC123 \
--target-id i-1234567890abcdef0
Lifecycle Hook Scripts Example
stop_server.sh (ApplicationStop)
#!/bin/bash
systemctl stop nginx
backup_config.sh (BeforeInstall)
#!/bin/bash
cp /etc/nginx/nginx.conf /backup/nginx.conf.$(date +%s)
install_dependencies.sh (AfterInstall)
#!/bin/bash
cd /var/www/html
npm install
start_server.sh (ApplicationStart)
#!/bin/bash
systemctl start nginx
validate.sh (ValidateService)
#!/bin/bash
curl -f http://localhost/ || exit 1
Common Commands
# Create application
aws deploy create-application --application-name MyApp
# Create deployment group
aws deploy create-deployment-group \
--application-name MyApp \
--deployment-group-name Production
# Create deployment
aws deploy create-deployment \
--application-name MyApp \
--deployment-group-name Production \
--s3-location bucket=my-bucket,key=app.zip,bundleType=zip
# Stop deployment
aws deploy stop-deployment --deployment-id d-ABC123
# List applications
aws deploy list-applications
# List deployment groups
aws deploy list-deployment-groups --application-name MyApp
# Get deployment status
aws deploy get-deployment --deployment-id d-ABC123
CodeDeploy + Auto Scaling Integration
Goal
Ensure newly launched Auto Scaling instances automatically get the latest application version deployed—without manual intervention.
How It Works
When you add an ASG to a Deployment Group, CodeDeploy automatically creates a lifecycle hook on that ASG. This hook intercepts instance launches.
Lifecycle hook: ASG feature that pauses instance state transitions (Pending → InService) to allow custom actions.
Flow (Scale-Out Event)
ASG launches new instance
│
▼
Lifecycle Hook (auto-created by CodeDeploy)
"Wait! Don't mark InService yet"
│
▼
CodeDeploy triggered
Deploys last successful revision
│
┌────┴────┐
│ │
▼ ▼
Success Failure
│ │
▼ ▼
InService Terminated
What Gets Deployed?
CodeDeploy deploys the last successful revision for that Deployment Group.
- If deployment #5 succeeded → new instances get revision #5
- If #5 failed but #4 succeeded → new instances get revision #4
Auto-Created Lifecycle Hook Details
| Property | Value |
|---|---|
| Hook name | CodeDeploy-managed-automatic-launch-deployment-hook-<app>-<dg> |
| Lifecycle transition | autoscaling:EC2_INSTANCE_LAUNCHING |
| Default timeout | 1 hour |
| Default result on timeout | ABANDON (instance terminated) |
Configuration Location
Configure from CodeDeploy side, not ASG side:
CodeDeploy Console → Application → Deployment Group → Edit → “Environment configuration” → Select Auto Scaling Group(s)
aws deploy update-deployment-group \
--application-name MyApp \
--current-deployment-group-name MyDG \
--auto-scaling-groups MyASG-Name \
--region us-east-1
The lifecycle hook appears in ASG Console (Instance Management → Lifecycle hooks) but is managed by CodeDeploy.
Detailed Deployment Mechanisms by Compute Platform
EC2/On-Premises: In-Place Deployment
What happens: CodeDeploy deploys to existing instances one-by-one (or in batches). Each instance has downtime during its deployment.
Required components:
- CodeDeploy Agent installed on instances
- IAM instance profile with S3 access (to download revision)
- Service role for CodeDeploy
Target selection (one of):
- EC2 tags (e.g.,
Environment=Production) - Auto Scaling Group name(s)
- On-premises instance tags
Optional:
- Load balancer (deregisters instance during deploy)
Flow:
Instance 1 Instance 2 Instance 3
│ │ │
▼ │ │
ApplicationStop │ │
│ │ │
▼ │ │
BeforeInstall │ │
│ │ │
▼ │ │
Install (copy files) │ │
│ │ │
▼ │ │
AfterInstall │ │
│ │ │
▼ │ │
ApplicationStart │ │
│ │ │
▼ │ │
ValidateService │ │
│ │ │
▼ ▼ │
Done ──────────► (same flow) ─────────► (same flow)
With Load Balancer:
Deregister from LB → Wait for drain → Deploy → Register to LB → Wait healthy
EC2: Blue/Green Deployment
What happens: CodeDeploy creates a NEW Auto Scaling Group (green), deploys to it, shifts traffic, then terminates old ASG (blue).
Required components:
- Auto Scaling Group (as target)
- Load Balancer (ALB or NLB) with Target Group
- Blue ASG instances already registered to Target Group
- CodeDeploy Agent in AMI or installed via user data
- Service role for CodeDeploy
Flow:
Step 1: Provision Green Fleet
─────────────────────────────
Original ASG (Blue) New ASG (Green)
┌─────────────────┐ ┌─────────────────┐
│ Instance 1 (v1) │ │ Instance 1 (v2) │ ← launched
│ Instance 2 (v1) │ │ Instance 2 (v2) │ ← launched
└────────┬────────┘ └────────┬────────┘
│ │
└──────── ALB ───────────────┘
(traffic still to blue)
Step 2: Deploy to Green
───────────────────────
CodeDeploy runs AppSpec lifecycle hooks on green instances
(BeforeInstall → Install → AfterInstall → ApplicationStart)
Step 3: Reroute Traffic
───────────────────────
ALB switches target group:
- Deregister blue instances from target group
- Register green instances to target group
Step 4: Terminate Blue (after wait time)
────────────────────────────────────────
Original ASG terminated (or kept for manual rollback)
Green fleet provisioning options:
COPY_AUTO_SCALING_GROUP: Clone blue ASG config (most common)DISCOVER_EXISTING: Use a pre-created ASG you specify
Configuration:
| Setting | What to specify |
|---|---|
| ASG | ASG name (not ARN) |
| Target Group | Target Group name (not ARN) |
| ALB/NLB | Not directly specified—inferred from Target Group |
aws deploy create-deployment-group \
--application-name MyApp \
--deployment-group-name MyDG \
--service-role-arn arn:aws:iam::123456789012:role/CodeDeployRole \
--auto-scaling-groups MyASG \
--load-balancer-info '{"targetGroupInfoList":[{"name":"my-target-group"}]}' \
--deployment-style deploymentType=BLUE_GREEN,deploymentOption=WITH_TRAFFIC_CONTROL \
--blue-green-deployment-configuration '{
"terminateBlueInstancesOnDeploymentSuccess":{"action":"TERMINATE","terminationWaitTimeInMinutes":5},
"deploymentReadyOption":{"actionOnTimeout":"CONTINUE_DEPLOYMENT","waitTimeInMinutes":0},
"greenFleetProvisioningOption":{"action":"COPY_AUTO_SCALING_GROUP"}
}' \
--region us-east-1
ECS: Blue/Green Deployment (Only deployment type available)
What happens: CodeDeploy creates new tasks (green) in the same ECS service, shifts traffic via load balancer, then stops old tasks (blue).
Key point: Same service, same cluster. Only the tasks change—CodeDeploy doesn’t create a new ECS service.
Required components:
- ECS Service (Fargate or EC2 launch type)
- ALB or NLB with two Target Groups (blue and green)
- ECS Service configured for
CODE_DEPLOYdeployment controller - AppSpec file (YAML or JSON)
Flow:
Initial State
─────────────
ECS Service (deployment controller: CODE_DEPLOY)
┌─────────────────────────────────────────────┐
│ Task 1 (task-def:v1) │ Task 2 (task-def:v1)
└────────────┬───────────┴──────────┬─────────┘
│ │
└──── Target Group A ──┘
│
ALB ──── Target Group B (empty)
(listener)
Step 1: Create Replacement Tasks (Green)
────────────────────────────────────────
ECS Service
┌─────────────────────────────────────────────┐
│ Task 1 (v1) │ Task 2 (v1) │ (blue) │
│ Task 3 (v2) │ Task 4 (v2) │ (green) │ ← new tasks
└───────────────┴───────────────┴─────────────┘
│ │
Target Group A Target Group B
(blue) (green)
Step 2: Run Test Traffic (Optional)
───────────────────────────────────
If test listener configured:
- Test listener (port 8443) → Target Group B (green)
- Run AfterAllowTestTraffic hook (validation)
Step 3: Shift Production Traffic
────────────────────────────────
ALB listener rule updated:
- Production listener → Target Group B (green)
Traffic shift options:
- AllAtOnce: 100% immediately
- Canary: 10% → wait → 100%
- Linear: 10% every N minutes
Step 4: Terminate Original Tasks
────────────────────────────────
After termination wait time:
- Task 1 (v1) stopped
- Task 2 (v1) stopped
- Target Group A now empty (ready for next deployment)
ECS AppSpec file:
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "arn:aws:ecs:us-east-1:123456789012:task-definition/my-task:2"
LoadBalancerInfo:
ContainerName: "my-container"
ContainerPort: 80
Hooks:
- BeforeInstall: "LambdaFunctionArn1"
- AfterInstall: "LambdaFunctionArn2"
- AfterAllowTestTraffic: "LambdaFunctionArn3"
- BeforeAllowTraffic: "LambdaFunctionArn4"
- AfterAllowTraffic: "LambdaFunctionArn5"
ECS lifecycle hooks (runs Lambda functions, not scripts):
| Hook | When it runs |
|---|---|
| BeforeInstall | Before new tasks created |
| AfterInstall | After new tasks running, before test traffic |
| AfterAllowTestTraffic | After test traffic routed (validation) |
| BeforeAllowTraffic | Before production traffic shift |
| AfterAllowTraffic | After production traffic shifted (final validation) |
Lambda: Blue/Green Deployment (Only deployment type available)
What happens: CodeDeploy shifts traffic between Lambda function versions using a Lambda alias.
Required components:
- Lambda function with versions published
- Lambda alias pointing to current version
- AppSpec file
No load balancer needed—Lambda alias handles traffic routing internally.
Flow:
Initial State
─────────────
Lambda Function: my-function
├── Version 1 (old code)
├── Version 2 (new code) ← just published
└── Alias "prod" → Version 1 (100%)
Invocations via alias: arn:aws:lambda:...:my-function:prod
Traffic Shift (depends on deployment config)
────────────────────────────────────────────
AllAtOnce:
Alias "prod" → Version 2 (100%) immediately
Canary10Percent5Minutes:
Alias "prod" → Version 1 (90%) + Version 2 (10%)
Wait 5 minutes...
Alias "prod" → Version 2 (100%)
Linear10PercentEvery1Minute:
Alias "prod" → Version 1 (90%) + Version 2 (10%)
Wait 1 minute...
Alias "prod" → Version 1 (80%) + Version 2 (20%)
... continues until 100%
Lambda AppSpec file:
version: 0.0
Resources:
- my-function:
Type: AWS::Lambda::Function
Properties:
Name: "my-function"
Alias: "prod"
CurrentVersion: "1"
TargetVersion: "2"
Hooks:
- BeforeAllowTraffic: "arn:aws:lambda:...:PreTrafficHook"
- AfterAllowTraffic: "arn:aws:lambda:...:PostTrafficHook"
Lambda lifecycle hooks (only two):
| Hook | When it runs |
|---|---|
| BeforeAllowTraffic | Validation before any traffic shifts |
| AfterAllowTraffic | Validation after traffic fully shifted |
Built-in deployment configurations for Lambda:
| Name | Behavior |
|---|---|
| Canary10Percent5Minutes | 10% for 5 min, then 100% |
| Canary10Percent10Minutes | 10% for 10 min, then 100% |
| Canary10Percent15Minutes | 10% for 15 min, then 100% |
| Linear10PercentEvery1Minute | +10% every minute |
| Linear10PercentEvery2Minutes | +10% every 2 minutes |
| Linear10PercentEvery3Minutes | +10% every 3 minutes |
| Linear10PercentEvery10Minutes | +10% every 10 minutes |
| AllAtOnce | 100% immediately |
Deployment Summary by Compute Platform
| Compute | Deployment Types | Traffic Shift Mechanism | Hooks Run On | Required Components |
|---|---|---|---|---|
| EC2/On-Prem | In-Place, Blue/Green | LB target group | Instance (scripts) | Agent, (LB for B/G) |
| ECS | Blue/Green only | LB target group | Lambda functions | ECS Service, ALB, 2 Target Groups |
| Lambda | Blue/Green only | Alias weighted routing | Lambda functions | Alias, Versions |
CodeDeploy Agent Installation via Systems Manager
Using SSM Run Command (One-time)
aws ssm send-command \
--document-name "AWS-ConfigureAWSPackage" \
--targets "Key=tag:Environment,Values=Production" \
--parameters '{"action":["Install"],"name":["AmazonCodeDeployAgent"]}' \
--region us-east-1
Using SSM State Manager (Continuous enforcement)
Auto-installs on new instances:
aws ssm create-association \
--name "AWS-ConfigureAWSPackage" \
--targets "Key=tag:Environment,Values=Production" \
--parameters '{"action":["Install"],"name":["AmazonCodeDeployAgent"]}' \
--schedule-expression "rate(1 day)" \
--region us-east-1
State Manager re-runs daily → any new instance gets agent automatically.
Best Practices
- Use blue/green deployment for production (zero downtime)
- Implement ValidateService hook to verify deployment success
- Use deployment configurations appropriate for your risk tolerance
- Enable automatic rollback on deployment failure or CloudWatch alarms
- Tag instances properly for deployment group targeting
- Test lifecycle hooks in non-production environment first
- Set appropriate timeouts for lifecycle event scripts
- Monitor deployments with CloudWatch and SNS notifications
- Use load balancer for blue/green deployments
- Keep blue instances for quick rollback capability
Pricing
- No additional charge for code deployments to EC2 or on-premises
- Lambda/ECS deployments: $0.02 per update (first 1,000 updates per month free)
- Additional costs: EC2 instances, S3 storage, data transfer
Notes
- CodeDeploy agent must be installed on EC2/on-premises instances
- Agent runs as a service and polls CodeDeploy for deployment instructions
- Lifecycle event scripts must exit with code 0 (success) or deployment fails
- Timeout causes lifecycle event to fail
- Can integrate with CodePipeline for automated CI/CD
- Supports rollback to previous revision
- Can deploy to instances across multiple regions
- Supports deployment to Auto Scaling groups