SerpentStack deploy
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.
git clone https://github.com/Benja-Pauls/SerpentStack
T=$(mktemp -d) && git clone --depth=1 https://github.com/Benja-Pauls/SerpentStack "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.skills/deploy" ~/.claude/skills/benja-pauls-serpentstack-deploy && rm -rf "$T"
.skills/deploy/SKILL.mdDeploy
Build, push, and deploy SerpentStack to AWS using Docker and Terraform.
Environments
-- automatic deploys from main, auto-approve on applydev
-- manual trigger, shows plan for review before applystaging
-- manual trigger, requires plan review before applyprod
All Terraform config lives in
infra/environments/{env}/.
Quick Deploy
make deploy # Deploy to dev (default) make deploy env=staging # Deploy to staging make deploy env=prod # Deploy to production
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:
# Find the previous working 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:
- All tests pass (
)make test - Migrations are included and tested on staging
- No secrets are hardcoded (check with
)git diff --cached | grep -i secret - Health check endpoint returns expected fields
- Rollback plan is documented with the previous known-good tag