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:

  1. Stop application on instance
  2. Install new version
  3. Start application
  4. 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:

  1. Create new instances (green environment)
  2. Deploy application to green instances
  3. Register green instances with load balancer
  4. Shift traffic from blue to green
  5. 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:

  1. ApplicationStop - Stop running application
  2. DownloadBundle - CodeDeploy downloads application revision (managed by CodeDeploy)
  3. BeforeInstall - Pre-installation tasks (backup, decrypt files)
  4. Install - Copy application files to destination (managed by CodeDeploy)
  5. AfterInstall - Post-installation tasks (set permissions, install dependencies)
  6. ApplicationStart - Start application services
  7. ValidateService - Verify application is running correctly

Blue/Green Deployment Additional Events

  1. BeforeBlockTraffic - Prepare for traffic removal
  2. BlockTraffic - Remove instance from load balancer (managed by CodeDeploy)
  3. AfterBlockTraffic - Verify no traffic reaching instance
  4. BeforeAllowTraffic - Final checks before receiving traffic
  5. AllowTraffic - Add instance to load balancer (managed by CodeDeploy)
  6. AfterAllowTraffic - Verify instance handling traffic correctly

Lambda Deployment Events

  1. BeforeAllowTraffic - Run tests against new version
  2. AllowTraffic - Shift traffic to new version (managed by CodeDeploy)
  3. 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

PropertyValue
Hook nameCodeDeploy-managed-automatic-launch-deployment-hook-<app>-<dg>
Lifecycle transitionautoscaling:EC2_INSTANCE_LAUNCHING
Default timeout1 hour
Default result on timeoutABANDON (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:

SettingWhat to specify
ASGASG name (not ARN)
Target GroupTarget Group name (not ARN)
ALB/NLBNot 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_DEPLOY deployment 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):

HookWhen it runs
BeforeInstallBefore new tasks created
AfterInstallAfter new tasks running, before test traffic
AfterAllowTestTrafficAfter test traffic routed (validation)
BeforeAllowTrafficBefore production traffic shift
AfterAllowTrafficAfter 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):

HookWhen it runs
BeforeAllowTrafficValidation before any traffic shifts
AfterAllowTrafficValidation after traffic fully shifted

Built-in deployment configurations for Lambda:

NameBehavior
Canary10Percent5Minutes10% for 5 min, then 100%
Canary10Percent10Minutes10% for 10 min, then 100%
Canary10Percent15Minutes10% for 15 min, then 100%
Linear10PercentEvery1Minute+10% every minute
Linear10PercentEvery2Minutes+10% every 2 minutes
Linear10PercentEvery3Minutes+10% every 3 minutes
Linear10PercentEvery10Minutes+10% every 10 minutes
AllAtOnce100% immediately

Deployment Summary by Compute Platform

ComputeDeployment TypesTraffic Shift MechanismHooks Run OnRequired Components
EC2/On-PremIn-Place, Blue/GreenLB target groupInstance (scripts)Agent, (LB for B/G)
ECSBlue/Green onlyLB target groupLambda functionsECS Service, ALB, 2 Target Groups
LambdaBlue/Green onlyAlias weighted routingLambda functionsAlias, 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

  1. Use blue/green deployment for production (zero downtime)
  2. Implement ValidateService hook to verify deployment success
  3. Use deployment configurations appropriate for your risk tolerance
  4. Enable automatic rollback on deployment failure or CloudWatch alarms
  5. Tag instances properly for deployment group targeting
  6. Test lifecycle hooks in non-production environment first
  7. Set appropriate timeouts for lifecycle event scripts
  8. Monitor deployments with CloudWatch and SNS notifications
  9. Use load balancer for blue/green deployments
  10. 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