| name | deploy |
| description | Build, push, and deploy to AWS using Docker, ECR, and Terraform. Use when: deploying to dev/staging/prod, checking deploy status, rolling back, or running the deploy checklist. |
Deploy
Build, push, and deploy SerpentStack to AWS using Docker and Terraform.
Environments
dev -- automatic deploys from main, auto-approve on apply
staging -- manual trigger, shows plan for review before apply
prod -- manual trigger, requires plan review before apply
All Terraform config lives in infra/environments/{env}/.
Quick Deploy
make deploy
make deploy env=staging
make deploy env=prod
Or use the GitHub Actions CD pipeline — it runs automatically on push to main when AWS credentials are configured as repository secrets.
Step-by-Step Manual Deploy
Step 1: Build Docker Images
docker build -t serpentstack-backend:latest ./backend
docker build -t serpentstack-frontend:latest ./frontend
Tag with the git SHA for traceability:
GIT_SHA=$(git rev-parse --short HEAD)
docker tag serpentstack-backend:latest serpentstack-backend:$GIT_SHA
docker tag serpentstack-frontend:latest serpentstack-frontend:$GIT_SHA
Step 2: Push to ECR
Authenticate with ECR (replace ACCOUNT_ID and REGION):
aws ecr get-login-password --region $AWS_REGION | \
docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
Tag and push both images:
docker tag serpentstack-backend:$GIT_SHA $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/serpentstack-backend:$GIT_SHA
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/serpentstack-backend:$GIT_SHA
docker tag serpentstack-frontend:$GIT_SHA $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/serpentstack-frontend:$GIT_SHA
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/serpentstack-frontend:$GIT_SHA
Step 3: Terraform Plan
cd infra/environments/{env}
terraform init
terraform plan -var="app_image_tag=$GIT_SHA" -out=tfplan
Review the plan output carefully. Look for:
- Resources being destroyed: unexpected destroys indicate a config drift. Investigate before proceeding.
- Security group changes: verify no ports are being opened unintentionally.
- Database modifications: any RDS changes should be treated as high-risk.
Step 4: Terraform Apply
Only after the plan is reviewed and approved:
cd infra/environments/{env}
terraform apply tfplan
For dev, the deploy script auto-approves. For staging and prod, always require explicit confirmation.
Step 5: Post-Deploy Health Check
Wait 30 seconds for App Runner to provision the new revision, then verify:
APP_URL=$(cd infra/environments/{env} && terraform output -raw app_url)
curl -sf $APP_URL/api/v1/health | jq .
Expected response:
{"status": "healthy", "version": "<version>"}
If the health check fails:
- Check App Runner service logs in the AWS console or via CLI:
aws apprunner list-operations --service-arn <arn>.
- Check if the new revision deployed:
aws apprunner describe-service --service-arn <arn> | jq '.Service.Status'.
- Check CloudWatch logs for the App Runner service.
Step 6: Rollback
If the deploy is broken, revert to the previous image tag:
PREV_TAG=$(git rev-parse --short HEAD~1)
cd infra/environments/{env}
terraform plan -var="app_image_tag=$PREV_TAG" -out=tfplan-rollback
terraform apply tfplan-rollback
Then verify the health check passes with the rolled-back version.
Checklist
Before deploying to prod: