# Do not change version. This is the buildspec version, not your file version.
version: 0.2 env: variables: AWS_REGION: "<aws_region>" # e.g. "ap-south-1" AWS_ACCOUNT_ID: "<aws_account_id>" # find this in your ECR repository URI IMAGE_REPO_NAME: "my-app" # your ECR repository name IMAGE_TAG: "latest" parameter-store: APP_NAME: "/my-app/APP_NAME" # add any other env variables here phases: pre_build: commands: - echo Logging in to Amazon ECR... - aws ecr get-login-password --region $AWS_REGION | -weight: 500;">docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com build: commands: - echo Build started on `date` - echo Building the Docker image... - -weight: 500;">docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - -weight: 500;">docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - -weight: 500;">docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
# Do not change version. This is the buildspec version, not your file version.
version: 0.2 env: variables: AWS_REGION: "<aws_region>" # e.g. "ap-south-1" AWS_ACCOUNT_ID: "<aws_account_id>" # find this in your ECR repository URI IMAGE_REPO_NAME: "my-app" # your ECR repository name IMAGE_TAG: "latest" parameter-store: APP_NAME: "/my-app/APP_NAME" # add any other env variables here phases: pre_build: commands: - echo Logging in to Amazon ECR... - aws ecr get-login-password --region $AWS_REGION | -weight: 500;">docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com build: commands: - echo Build started on `date` - echo Building the Docker image... - -weight: 500;">docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - -weight: 500;">docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - -weight: 500;">docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
# Do not change version. This is the buildspec version, not your file version.
version: 0.2 env: variables: AWS_REGION: "<aws_region>" # e.g. "ap-south-1" AWS_ACCOUNT_ID: "<aws_account_id>" # find this in your ECR repository URI IMAGE_REPO_NAME: "my-app" # your ECR repository name IMAGE_TAG: "latest" parameter-store: APP_NAME: "/my-app/APP_NAME" # add any other env variables here phases: pre_build: commands: - echo Logging in to Amazon ECR... - aws ecr get-login-password --region $AWS_REGION | -weight: 500;">docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com build: commands: - echo Build started on `date` - echo Building the Docker image... - -weight: 500;">docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - -weight: 500;">docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - -weight: 500;">docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
#!/bin/bash
echo "Before -weight: 500;">install"
#!/bin/bash
echo "Before -weight: 500;">install"
#!/bin/bash
echo "Before -weight: 500;">install"
#!/bin/bash
echo "Application -weight: 500;">stop"
#!/bin/bash
echo "Application -weight: 500;">stop"
#!/bin/bash
echo "Application -weight: 500;">stop"
#!/bin/bash cd /home/ubuntu AWS_ACCOUNT_ID="<aws_account_id>"
AWS_REGION="<aws_region>"
IMAGE_TAG="latest"
APP_NAME="my-app"
PORT="8000" # Pull latest image from ECR
aws ecr get-login-password --region $AWS_REGION | -weight: 500;">docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
-weight: 500;">docker pull $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$APP_NAME:$IMAGE_TAG # Refresh .env from Parameter Store
aws ssm get-parameters-by-path \ --path "/${APP_NAME}" --recursive --with-decrypt \ | jq -r '.Parameters[] | (.Name | split("/")[-1]) + "=" + (.Value)' \ | tee /home/ubuntu/.env # Stop existing containers and clean up
-weight: 500;">docker -weight: 500;">stop $(-weight: 500;">docker ps -q)
-weight: 500;">docker system prune -f # Start the updated container
-weight: 500;">docker run --env-file .env -p $PORT:$PORT -d $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$APP_NAME:$IMAGE_TAG # Restart nginx
-weight: 600;">sudo nginx -t
-weight: 600;">sudo -weight: 500;">service nginx -weight: 500;">restart
#!/bin/bash cd /home/ubuntu AWS_ACCOUNT_ID="<aws_account_id>"
AWS_REGION="<aws_region>"
IMAGE_TAG="latest"
APP_NAME="my-app"
PORT="8000" # Pull latest image from ECR
aws ecr get-login-password --region $AWS_REGION | -weight: 500;">docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
-weight: 500;">docker pull $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$APP_NAME:$IMAGE_TAG # Refresh .env from Parameter Store
aws ssm get-parameters-by-path \ --path "/${APP_NAME}" --recursive --with-decrypt \ | jq -r '.Parameters[] | (.Name | split("/")[-1]) + "=" + (.Value)' \ | tee /home/ubuntu/.env # Stop existing containers and clean up
-weight: 500;">docker -weight: 500;">stop $(-weight: 500;">docker ps -q)
-weight: 500;">docker system prune -f # Start the updated container
-weight: 500;">docker run --env-file .env -p $PORT:$PORT -d $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$APP_NAME:$IMAGE_TAG # Restart nginx
-weight: 600;">sudo nginx -t
-weight: 600;">sudo -weight: 500;">service nginx -weight: 500;">restart
#!/bin/bash cd /home/ubuntu AWS_ACCOUNT_ID="<aws_account_id>"
AWS_REGION="<aws_region>"
IMAGE_TAG="latest"
APP_NAME="my-app"
PORT="8000" # Pull latest image from ECR
aws ecr get-login-password --region $AWS_REGION | -weight: 500;">docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
-weight: 500;">docker pull $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$APP_NAME:$IMAGE_TAG # Refresh .env from Parameter Store
aws ssm get-parameters-by-path \ --path "/${APP_NAME}" --recursive --with-decrypt \ | jq -r '.Parameters[] | (.Name | split("/")[-1]) + "=" + (.Value)' \ | tee /home/ubuntu/.env # Stop existing containers and clean up
-weight: 500;">docker -weight: 500;">stop $(-weight: 500;">docker ps -q)
-weight: 500;">docker system prune -f # Start the updated container
-weight: 500;">docker run --env-file .env -p $PORT:$PORT -d $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$APP_NAME:$IMAGE_TAG # Restart nginx
-weight: 600;">sudo nginx -t
-weight: 600;">sudo -weight: 500;">service nginx -weight: 500;">restart
version: 0.0
os: linux
files: - source: / destination: /home/ubuntu/my-app overwrite: true
hooks: ApplicationStart: - location: aws-scripts/application_start.sh timeout: 3000 runas: ubuntu
file_exists_behavior: OVERWRITE
version: 0.0
os: linux
files: - source: / destination: /home/ubuntu/my-app overwrite: true
hooks: ApplicationStart: - location: aws-scripts/application_start.sh timeout: 3000 runas: ubuntu
file_exists_behavior: OVERWRITE
version: 0.0
os: linux
files: - source: / destination: /home/ubuntu/my-app overwrite: true
hooks: ApplicationStart: - location: aws-scripts/application_start.sh timeout: 3000 runas: ubuntu
file_exists_behavior: OVERWRITE - Part 1 - Dockerize and deploy your Node.js app with env on AWS EC2
- Part 2 - Auto scale and load balance your Dockerized app on AWS - Trigger AWS CodeBuild to build a new Docker image and push it to ECR
- Trigger AWS CodeDeploy via AWS CodePipeline to deploy the new image to every running EC2 instance - Defines the AWS account variables and ECR repository details
- Reads environment variables from Parameter Store (the same ones from Part 1)
- Logs in to ECR, builds the Docker image and pushes the new image - Under Trusted entity type, select AWS -weight: 500;">service
- Under Use case, select CodeBuild
- Add these permission policies: AmazonEC2ContainerRegistryFullAccess, AmazonSSMFullAccess
- Name the role my-app-code-build-role
- Click Create role - Project name: my-app-code-build, Project type: Default
- Source: Choose GitHub, connect your GitHub account and select your repository - Environment: On-demand, Managed image, EC2, Container
- Operating system: Ubuntu, Standard runtime
- Service role: Choose the my-app-code-build-role we just created - Buildspec: Choose Use a buildspec file — this picks up the buildspec.yml from the repository
- Artifacts: No artifacts — we only need to push to ECR
- Click Create project - Under Trusted entity type, select AWS -weight: 500;">service
- Under Use case, select CodeDeploy
- Keep the default AWSCodeDeployRole permission that is pre-selected
- Name the role my-app-code-deploy-role
- Click Create role - AmazonEC2ContainerRegistryFullAccess — to pull images from ECR
- AmazonS3FullAccess — to access pipeline artifacts from S3
- AutoScalingFullAccess — to interact with the Auto Scaling Group
- AWSCodePipeline_FullAccess — to work within the pipeline - Application name: my-app-code-deployment-application
- Compute platform: EC2/On-premises - Deployment group name: my-app-deployment-group
- Service role: Choose my-app-code-deploy-role - Environment configuration: Select Amazon EC2 Auto Scaling groups and choose my-app-asg (the Auto Scaling Group from Part 2)
- Deployment settings: CodeDeployDefault.AllAtOnce - Load balancer: Uncheck Enable load balancing - Creation options: Build custom pipeline
- Pipeline name: my-app-code-pipeline, Execution mode: Queued
- Service role: Create a new -weight: 500;">service role — no need to reuse an existing one. Click next. - Source: Choose GitHub (via GitHub App), select your repository and main branch
- Under Webhook events, select Start your pipeline on push or pull request event. Under Webhook event filters, set Event type to Push, Filter type to Branch and value to main. This ensures only pushes to main fire the pipeline. Click next. - Build stage: Under Other build providers, choose AWS CodeBuild and select my-app-code-build. No environment variables needed — they come from Parameter Store. Set artifacts to No artifacts. Click next. - Test stage: Skip this stage.
- Deploy stage: Choose AWS CodeDeploy. Select my-app-code-deployment-application as the application and my-app-deployment-group as the deployment group. Click next. - Review everything and click Create pipeline. - Push a code change to the main branch on GitHub
- Watch CodeBuild pick up the push, build the Docker image and push it to ECR
- Watch CodeDeploy roll out the new image to your running EC2 instances
- Visit your load balancer DNS (my-app-lb-xxxxxxxx.<aws_region>.elb.amazonaws.com) to see the updated application - Part 1: Dockerized the app, pushed it to ECR and deployed it to a single EC2 instance with environment variables managed in Parameter Store
- Part 2: Created a base AMI, a launch template, an Application Load Balancer and an Auto Scaling Group so the app scales automatically
- Part 3: Set up a full CI/CD pipeline — every push to main now automatically builds a new Docker image and deploys it to every running instance