AWS CodeDeploy EC2/On-Premises Deployment - Designing AppSpec and Lifecycle Hooks
Declaratively define deployment target paths and lifecycle hooks using AppSpec files. Learn about fleet management with tag-based deployment groups and Auto Scaling integration.
Structure of the AppSpec File
The AppSpec file (appspec.yml) for EC2/on-premises deployments uses the files section to define deployment target paths for source files, the permissions section to set file ownership and permissions, and the hooks section to specify scripts that run at each stage of the lifecycle. In the files section, you define mappings between source (path within the revision) and destination (path on the instance). In the permissions section, you specify owner, group, and mode to properly configure permissions for deployed files. The version field is fixed at 0.0 and is reserved by AWS for future extensions. The AppSpec file must be placed in the root directory of the revision; missing this is a common cause of deployment failure. When a directory is specified as the destination in the files section, the source directory structure is replicated as-is. For single-file deployments, specifying the filename in both source and destination enables rename deployments.
Using Lifecycle Hooks
Lifecycle hooks are scripts that run at each stage of the deployment. BeforeInstall runs before files are deployed and is used for cleaning up old versions or installing dependency packages. AfterInstall runs after files are deployed and is used for generating configuration files or running database migrations. ApplicationStart executes the application startup script. ValidateService runs post-deployment health checks to verify that the application is functioning correctly. If ValidateService fails, the deployment is recorded as failed, and if automatic rollback is configured, it reverts to the previous version. Each hook has a configurable timeout (default 3600 seconds), and it is common to extend the timeout for long-running migration processes. ApplicationStop is used to stop the previous version, but note that it is not executed during the first deployment since no previous version exists. The environment variables DEPLOYMENT_ID and DEPLOYMENT_GROUP_NAME are available within hooks, enabling conditional behavior per deployment.
Deployment Groups and Fleet Management
A deployment group is a unit that groups the instances targeted for deployment. Tag filters allow dynamic selection, such as choosing instances where Environment:Production and Role:WebServer. With Auto Scaling group integration, the latest revision is automatically deployed to newly launched instances during scale-out. By specifying the minimum healthy hosts in the deployment configuration, you ensure that a certain number of instances continue processing requests during deployment. CodeDeployDefault.OneAtATime deploys one instance at a time, while CodeDeployDefault.HalfAtATime deploys half at a time as predefined configurations. For a deeper understanding of EC2 deployment practices, specialized books (Amazon) can be helpful.
Design Best Practices and Pitfalls
Hook scripts in AppSpec should be designed to be idempotent. Since the same script may run multiple times after a failed deployment retry or rollback, scripts must not fail on stopping an already-stopped service or creating an already-existing directory. In ValidateService hooks, incorporating warmup waits or retry logic in addition to HTTP health checks prevents deployment failures caused by transient startup failures. With tag-based deployment groups, accidental tag reassignment can cause deployments to unintended instances. It is recommended to add an approval step in CodePipeline to review the target instance list before deployment. When integrated with Auto Scaling, if deployment fails on a newly launched instance, Auto Scaling may mark it unhealthy and terminate it, creating an infinite loop. To prevent this, configure EC2 Auto Scaling lifecycle hooks to wait for deployment completion before transitioning to InService.
CodePipeline Integration and Rollback Strategy
Combining with CodePipeline automates the entire workflow from source code change detection through build, test, and deployment. Inserting manual approval actions between pipeline stages provides a review gate before production deployments. CodeDeploy's rollback capability is a critical mechanism for deployment safety - when ValidateService hooks or health checks fail during deployment, it automatically reverts to the previous version. Integration with CloudWatch Alarms enables triggering rollbacks when post-deployment error rates increase or latency rises. With Blue/Green deployments, configuring the old environment to be retained for a set period guarantees immediate rollback when issues are discovered. Deployment logs are automatically sent to CloudWatch Logs, serving as data for failure analysis and performance improvement.
Comparing CodeDeploy with Other Deployment Methods
Deployment options for EC2 include CodeDeploy, Systems Manager Run Command, configuration management tools (Ansible/Chef), and containerization (ECS/EKS). CodeDeploy uses agent-based pull deployments, eliminating the need for SSH access or security group modifications. Systems Manager Run Command suits ad-hoc command execution but requires custom implementation for rollback and staged deployment control. Ansible/Chef excel at configuration management idempotency but lack AWS-native deployment progress management (minimum healthy hosts, automatic rollback). Containerization is the ideal for immutable infrastructure but carries high migration costs for legacy applications. For existing workloads on EC2/on-premises, CodeDeploy offers the lowest-cost incremental adoption. The ability to include on-premises servers as deployment targets is a unique CodeDeploy strength, making it the only AWS-native option for building a unified deployment pipeline in hybrid environments.
CodeDeploy Pricing
CodeDeploy EC2/on-premises deployments are free to use. There are no additional charges for deploying to EC2 instances. Deployments to on-premises servers cost approximately $0.02 per instance per deployment. Deployments to ECS and Lambda are also free. Since CodeDeploy itself costs virtually nothing, it has a low barrier to adoption as a deployment stage in CI/CD pipelines. However, S3 storage for revisions, CodePipeline fees (approximately $1 per pipeline per month) for deployment triggers, and SNS fees for notifications are billed separately. For large fleets (hundreds of instances or more), the per-deployment charge for on-premises deployments becomes non-trivial, so you should estimate costs based on the product of monthly deployment count and instance count.
Summary
CodeDeploy EC2/on-premises deployments declaratively define the deployment process using AppSpec files and lifecycle hooks. Tag-based deployment groups handle dynamic fleets, and minimum healthy hosts ensure availability during deployment. Since on-premises servers can also be included as targets, it is effective for unified deployment management in hybrid environments. By ensuring idempotent script design and proper Auto Scaling lifecycle hook coordination, you can achieve stable zero-downtime deployments.