AWS – CodeDeploy: Blue/Green and Where It Still Fits

July 8, 20254 min readUpdated 8/24/2026

CodeDeploy is the piece of AWS's CI/CD family that takes a built artifact and puts it on the thing that runs it. CodeBuild produces the artifact, CodePipeline sequences the stages, CodeDeploy does the last step — and it is the one with an opinion worth having, because it knows how to shift traffic gradually and take it back.

Where it still earns its place in 2026

Being clear about this first saves you evaluating it for a job it is not good at.

TargetVerdict
EC2 / on-premisesYes. Still the best answer for deploying to instances you own
ECS blue/greenYes. ECS's own rolling update cannot shift traffic in weighted steps or roll back on an alarm
LambdaSometimes — canary and linear traffic shifting, usually reached through SAM's DeploymentPreference rather than directly
Static sites, containers on Fargate via rolling updateNo. Nothing to gain

If you deploy by replacing a container image and you are happy with a rolling update, you do not need CodeDeploy. If a bad deploy needs to be caught by an alarm and undone in seconds without a human, that is exactly what it is for.

appspec: the file that describes a deployment

Every deployment is driven by an appspec file that sits in the artifact. On EC2 it is YAML, it lists what to copy where, and it names the scripts to run at each stage:

version: 0.0
os: linux
files:
  - source: /
    destination: /srv/stayhub
hooks:
  ApplicationStop:
    - location: scripts/stop.sh
      timeout: 60
      runas: root
  BeforeInstall:
    - 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

On ECS and Lambda the appspec is a different, smaller shape — it names the task definition or function version rather than files — but the hook idea is the same.

The hooks, and the order that surprises people

The lifecycle runs roughly: ApplicationStopBeforeInstallInstallAfterInstallApplicationStartValidateService.

Two things about that sequence are worth knowing before your first deployment hangs.

ApplicationStop runs the script from the PREVIOUS deployment, not the one you are shipping. It has to, because the new files are not on the instance yet. So a broken stop.sh keeps breaking every future deployment until you fix it on the instance by hand — the fix in your new revision never gets a chance to run.

ValidateService is the point of the whole file. It is the hook where you check that the thing you just started actually works — curl the health endpoint, verify it returns 200, exit non-zero if not. A deployment with no ValidateService succeeds as long as the process launched, which is not the same as working. Blue/green is only safer than in-place if something is qualified to say the new version is bad.

In-place versus blue/green

In-place updates the instances you already have, taking them out of the load balancer a batch at a time. Cheap, and rollback means deploying the old revision again — which takes as long as a deploy.

Blue/green stands up a replacement set, waits, shifts traffic, and keeps the originals for a while. It costs double the instances during the deployment and rollback is a traffic shift back, measured in seconds. On ECS the same idea uses two target groups rather than two fleets.

Pick blue/green when a bad minute is expensive. Pick in-place when it is not.

Automatic rollback is the feature

Manual rollback is just another deploy. The reason to configure CodeDeploy properly is that it can watch a CloudWatch alarm and undo itself:

aws deploy update-deployment-group \
  --application-name stayhub \
  --current-deployment-group-name prod \
  --auto-rollback-configuration "enabled=true,events=DEPLOYMENT_FAILURE,DEPLOYMENT_STOP_ON_ALARM" \
  --alarm-configuration "enabled=true,alarms=[{name=stayhub-5xx-high}]"

Now a spike in 5xx during a deployment reverses it without anyone being paged. That is the capability you are buying, and it is worth more than the deployment mechanics.

Deployment configurations

How fast traffic moves is a named, reusable setting rather than something you script. The built-in ones cover most needs:

NameBehaviour
AllAtOnceEverything at once. Fast, and a bad revision is a full outage
HalfAtATime50% then 50%. Needs the remaining half to carry the load
OneAtATimeSafest and slowest; a large fleet takes a long time
Canary10Percent5Minutes10% of traffic, wait 5 minutes, then the rest
Linear10PercentEvery1Minute10% more each minute

The canary and linear configurations are the ones that pair with an alarm: the waiting period exists so the alarm has time to fire while most traffic is still on the old version. A canary with no alarm attached is just a slower deploy — the pause is only useful if something is watching.

Why a deployment hangs for an hour

The classic failure: the deployment sits at ApplicationStop or BeforeInstall and eventually times out.

Almost always it is the CodeDeploy agent — not installed, not running, or the instance's role cannot read the artifact from S3. The agent polls for work, so if it is not running nothing reports back and the deployment waits for its timeout rather than failing fast. Check the agent first:

aws deploy get-deployment --deployment-id d-EXAMPLE123 \
  --query "deploymentInfo.[status,errorInformation]"

The agent's own log on the instance, at /var/log/aws/codedeploy-agent/codedeploy-agent.log, says more than the console does — it is the first place to look, not the last. Hook script output goes to a separate directory, /opt/codedeploy-agent/deployment-root/deployment-logs/, which is where you find out why healthcheck.sh exited non-zero.