Skip to content
AWS aws deployment 5 min read

AWS CodeDeploy

AWS CodeDeploy is a managed service that pushes your application code onto compute targets for you, so you never have to SSH into a box and copy files by hand. It works on EC2 (Elastic Compute Cloud, AWS virtual servers), on-premises servers, Amazon ECS (Elastic Container Service), and AWS Lambda (functions that run without servers). You describe what to deploy and what to run at each step in a small file called an appspec, and CodeDeploy orchestrates the rollout across all your targets, watching for failures and rolling back when something breaks. This matters because manual deploys are slow, error-prone, and impossible to repeat safely across dozens of instances.

How CodeDeploy works

CodeDeploy has a few core pieces. An application is a logical name for the thing you deploy. A deployment group is the set of targets that receive a deployment (for example, a group of EC2 instances tagged Environment=prod, an ECS service, or a Lambda function alias). A deployment configuration controls how fast the rollout happens and how many failures are tolerated. The revision is the bundle of files plus an appspec file that CodeDeploy actually ships.

The appspec file (AppSpec means “application specification”) is the heart of it. For EC2/on-prem it is appspec.yml and lists files to copy plus lifecycle event hooks (scripts to run at named stages). For ECS and Lambda it is appspec.yaml and points at task definitions or function versions instead of copying files.

EC2 lifecycle hooks

On EC2, the deployment runs through ordered stages. You attach scripts to the ones you need:

HookWhen it runsTypical use
ApplicationStopBefore the new revision arrivesStop the running app gracefully
BeforeInstallBefore files are copiedBack up files, set permissions
AfterInstallAfter files are copiedInstall dependencies, run migrations
ApplicationStartStart the appsystemctl start myapp
ValidateServiceLast stepHealth check the new version

Gotcha: Hook scripts run as the user you set in runas, with the working directory at the deployment archive root. A wrong path or a script that is not chmod +x makes the hook fail. If you reference a hook stage but the script is missing, the deployment errors out, not silently skips it.

In-place vs blue/green deployments

CodeDeploy supports two strategies. Choosing the right one is the most important decision you make.

StrategyWhat happensWhen to useWhen NOT to use
In-placeNew code replaces old code on the same instancesSimple apps, dev/test, where brief downtime is fineProduction where zero downtime matters; Lambda (not supported there)
Blue/greenNew “green” fleet is created, traffic shifts over, old “blue” fleet is terminatedProduction EC2/ECS/Lambda needing zero downtime and fast rollbackTight budgets or limited capacity — you pay for two fleets during the cutover

Cost and capacity warning: Blue/green needs spare capacity. For EC2 it provisions a whole second set of instances (you briefly pay for double), and for ECS it needs a second target group on your load balancer. If your account hits an instance limit or has no spare target group, the deployment cannot start. Always confirm your deployment configuration and that rollback triggers actually fire — an unmonitored auto-rollback can mask a real failure by reporting “succeeded after retry” when the bad version was simply replaced silently.

When to use CodeDeploy (and when not to)

Use CodeDeploy when you have your own EC2 instances, on-prem servers, ECS services, or Lambda functions and want repeatable, monitored rollouts with automatic rollback. It pairs naturally with CodePipeline as the deploy stage of a CI/CD pipeline.

Do not reach for CodeDeploy if Elastic Beanstalk or a fully managed platform already handles deploys for you, or if your whole app is defined as infrastructure in CloudFormation/CDK where stack updates do the job. It is a deploy orchestrator, not a build tool — use CodeBuild to produce the artifact first.

Setting up an EC2 deployment

For EC2 you must install and run the CodeDeploy agent (a small daemon on each instance that talks to the service) and attach an IAM role that allows the instance to read your artifact from S3.

The #1 gotcha: If the CodeDeploy agent is not installed or not running on an instance, that instance is invisible to CodeDeploy. The deployment can show as succeeded while doing nothing on that box. Always verify the agent with sudo systemctl status codedeploy-agent.

Console steps

  1. Open the CodeDeploy console and choose Applications then Create application.
  2. Enter a name and choose compute platform EC2/On-premises.
  3. Inside the app, choose Create deployment group. Give it a name and a service role that allows CodeDeploy access.
  4. Pick In-place or Blue/green, then select targets by Amazon EC2 tags (e.g. key Environment, value prod).
  5. Choose a deployment configuration (e.g. CodeDeployDefault.OneAtATime) and optionally enable a load balancer and rollback triggers.
  6. Choose Create deployment, point it at your S3 bundle or GitHub revision, and Deploy.

CLI equivalent

aws deploy create-application \
  --application-name MyWebApp \
  --compute-platform Server

aws deploy create-deployment-group \
  --application-name MyWebApp \
  --deployment-group-name prod \
  --service-role-arn arn:aws:iam::111122223333:role/CodeDeployServiceRole \
  --deployment-config-name CodeDeployDefault.OneAtATime \
  --ec2-tag-filters Key=Environment,Value=prod,Type=KEY_AND_VALUE

aws deploy create-deployment \
  --application-name MyWebApp \
  --deployment-group-name prod \
  --s3-location bucket=my-artifacts,key=app/build-42.zip,bundleType=zip

Output:

{
    "deploymentId": "d-A1B2C3D4E"
}

Track it until it finishes:

aws deploy get-deployment --deployment-id d-A1B2C3D4E \
  --query 'deploymentInfo.status'

Output:

"Succeeded"

A minimal appspec.yml

version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/myapp
hooks:
  AfterInstall:
    - location: scripts/install_deps.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: scripts/start.sh
      timeout: 60
      runas: root
  ValidateService:
    - location: scripts/healthcheck.sh
      timeout: 120
      runas: root

ECS and Lambda deployments

For ECS and Lambda there are no files to copy. The appspec references the new version, and CodeDeploy shifts traffic gradually using configs like CodeDeployDefault.ECSLinear10PercentEvery1Minutes or CodeDeployDefault.LambdaCanary10Percent5Minutes. A canary sends a small slice of traffic first; linear shifts in equal steps. If a CloudWatch alarm fires during the shift, the rollback trigger reverts traffic to the old version automatically.

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:us-east-1:111122223333:task-definition/web:42"
        LoadBalancerInfo:
          ContainerName: "web"
          ContainerPort: 80

Best practices

  • Always confirm the CodeDeploy agent is installed and running on every EC2 target before your first deploy.
  • Add a real ValidateService hook (or CloudWatch alarm for ECS/Lambda) so a broken release actually fails instead of passing silently.
  • Enable automatic rollback on failure, but monitor the rollback events — do not treat “succeeded after retry” as healthy without checking logs.
  • Start non-prod with CodeDeployDefault.OneAtATime to catch bad releases on one instance before they spread.
  • For zero-downtime production, use blue/green and confirm you have spare instance capacity or a second target group before deploying.
  • Keep hook scripts small, idempotent, and executable (chmod +x), and reference them by paths relative to the bundle root.
Last updated June 15, 2026
Was this helpful?