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:
| Hook | When it runs | Typical use |
|---|---|---|
ApplicationStop | Before the new revision arrives | Stop the running app gracefully |
BeforeInstall | Before files are copied | Back up files, set permissions |
AfterInstall | After files are copied | Install dependencies, run migrations |
ApplicationStart | Start the app | systemctl start myapp |
ValidateService | Last step | Health 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 notchmod +xmakes 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.
| Strategy | What happens | When to use | When NOT to use |
|---|---|---|---|
| In-place | New code replaces old code on the same instances | Simple apps, dev/test, where brief downtime is fine | Production where zero downtime matters; Lambda (not supported there) |
| Blue/green | New “green” fleet is created, traffic shifts over, old “blue” fleet is terminated | Production EC2/ECS/Lambda needing zero downtime and fast rollback | Tight 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
- Open the CodeDeploy console and choose Applications then Create application.
- Enter a name and choose compute platform EC2/On-premises.
- Inside the app, choose Create deployment group. Give it a name and a service role that allows CodeDeploy access.
- Pick In-place or Blue/green, then select targets by Amazon EC2 tags (e.g. key
Environment, valueprod). - Choose a deployment configuration (e.g.
CodeDeployDefault.OneAtATime) and optionally enable a load balancer and rollback triggers. - 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
ValidateServicehook (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.OneAtATimeto 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.