بنقرة واحدة
ec2-deploy
// Deploy to EC2 production server. Use when deploying new code, rebuilding Docker images, wiping and reseeding the database, checking container health, or managing the EC2 instance via AWS CLI.
// Deploy to EC2 production server. Use when deploying new code, rebuilding Docker images, wiping and reseeding the database, checking container health, or managing the EC2 instance via AWS CLI.
Discover any website's API and create domain plugins with proxy routes. Use when the user wants to create an API for a website, discover a web service's data transport, add a new domain, capture browser traffic, build typed API clients, or integrate with a third-party site. Also use when the user mentions a website name and wants to interact with it programmatically.
Build a complete application from a short description. Asks the developer clarifying questions, generates data requirements, launches discovery agents, and builds a dashboard. Use when the developer describes what they want to build in plain language — "compare tickets", "track prices", "search jobs across sites".
Run local CI checks and verify GitHub Actions status. Use before committing, after pushing, or when asked to check/fix CI.
Build Next.js dashboard pages that consume domain proxy APIs. Use when the user wants to create a dashboard, build a UI page, add a search interface, display data from captured APIs, create comparison views, or build any frontend that calls /api/<domain>/ endpoints.
Iterative debugging with targeted logs. Use when browser connections fail, traffic capture returns empty, proxy routes return errors, WebSocket issues, or any problem that can't be solved on the first attempt. Add logs, read output, narrow the search, repeat until fixed, then clean up.
Use sub-agents to iteratively improve dashboard-building instructions. Three-phase pipeline — discover APIs, build dashboard matching a wireframe, review code + UI with a SOTA reviewer agent. The dashboards are throwaway; instruction improvements and framework code fixes are the product.
| name | ec2-deploy |
| description | Deploy to EC2 production server. Use when deploying new code, rebuilding Docker images, wiping and reseeding the database, checking container health, or managing the EC2 instance via AWS CLI. |
Check if .claude/user-consent.md exists with ACCEPTED: true. If yes, display: ✅ Prior consent on file (DATE). Proceeding. and skip to "Config."
If not, present the 3 warnings from .claude/skills/instruction-tuning/SKILL.md (ToS, autonomous agents, resource consumption). All 3 must be accepted. Write .claude/user-consent.md on acceptance. This file is shared across all skills that affect external infrastructure.
Manage EC2 instances and Docker Compose services using the AWS CLI and SSH.
Use this skill when asked to:
Configure these values based on your deployment environment. All commands below reference them by name.
| Variable | Description | Example |
|---|---|---|
INSTANCE_NAME | EC2 instance name tag | my-app-prod |
SSH_KEY | Local SSH private key | ~/.ssh/my-app-prod-key.pem |
SSH_USER | OS user on instance | ubuntu (or ec2-user) |
PROJECT_DIR | Git repo root on instance | /home/ubuntu/my-project |
COMPOSE_FILE | Docker Compose config | docker-compose.prod.yml |
DB_USER | Database username | appuser |
DB_NAME | Database name | app_db |
Get your INSTANCE_ID from AWS console or aws ec2 describe-instances --filters "Name=tag:Name,Values=$INSTANCE_NAME".
ALWAYS start here. Never hardcode IPs — use AWS CLI to get the current public IP:
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--query "Reservations[*].Instances[*].{ID:InstanceId,Name:Tags[?Key=='Name']|[0].Value,IP:PublicIpAddress,Type:InstanceType,State:State.Name}" \
--output table
Find your instance name from the output above. If its public IP has changed, this command will always show the current one.
ssh -i $SSH_KEY -o StrictHostKeyChecking=no $SSH_USER@<PUBLIC_IP>
$SSH_KEY (configure before use)$SSH_USER (typically ubuntu for Ubuntu, ec2-user for Amazon Linux)$PROJECT_DIR (your git repo path)Verify the key before SSHing:
aws ec2 describe-instances \
--instance-ids $INSTANCE_ID \
--query "Reservations[0].Instances[0].KeyName" \
--output text
Docker containers managed via docker-compose.prod.yml:
| Container | Image | Purpose |
|---|---|---|
app_db | timescale/timescaledb or postgres | PostgreSQL database |
app_redis | redis:7-alpine | Job queue (BullMQ) |
app_api | your-app-api (local build) | API server |
app_web | your-app-web (local build) | Frontend (Next.js or similar) |
app_proxy | caddy:2-alpine | Reverse proxy (80/443) |
Key facts:
docker exec or within Docker network.env file is at $PROJECT_DIR/.env (not in the Docker image)docker-compose.prod.ymlWhen only code changes (no schema or seed changes):
ssh -i $SSH_KEY $SSH_USER@<IP> "
cd $PROJECT_DIR
git pull origin main
docker compose -f $COMPOSE_FILE build api web
docker compose -f $COMPOSE_FILE up -d
"
When schema changed or a clean slate is needed.
git push origin main
ssh -i $SSH_KEY $SSH_USER@<IP> "
cd $PROJECT_DIR && git pull origin main && git log --oneline -3
"
ssh -i $SSH_KEY $SSH_USER@<IP> "
docker exec app_db psql -U $DB_USER -d postgres \
-c \"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='$DB_NAME' AND pid<>pg_backend_pid();\"
docker exec app_db psql -U $DB_USER -d postgres \
-c 'DROP DATABASE IF EXISTS $DB_NAME;'
docker exec app_db psql -U $DB_USER -d postgres \
-c 'CREATE DATABASE $DB_NAME OWNER $DB_USER;'
"
ssh -i $SSH_KEY $SSH_USER@<IP> "
cd $PROJECT_DIR
docker compose -f $COMPOSE_FILE stop api web
"
ssh -i $SSH_KEY $SSH_USER@<IP> "
cd $PROJECT_DIR
docker compose -f $COMPOSE_FILE build api web
"
Takes 3–8 minutes depending on dependency installations.
ssh -i $SSH_KEY $SSH_USER@<IP> "
cd $PROJECT_DIR
docker compose -f $COMPOSE_FILE run --rm --no-deps api \
sh -c 'cd /app && pnpm run db:migrate && pnpm run db:seed'
"
Adjust migration/seed commands to match your project setup.
ssh -i $SSH_KEY $SSH_USER@<IP> "
cd $PROJECT_DIR
docker compose -f $COMPOSE_FILE up -d
"
# Container health
ssh -i $SSH_KEY $SSH_USER@<IP> \
"docker ps --format 'table {{.Names}}\t{{.Status}}'"
# API health
curl -s https://your-domain.com/api/health
# DB check
ssh -i $SSH_KEY $SSH_USER@<IP> "
docker exec app_db psql -U $DB_USER -d $DB_NAME -c 'SELECT COUNT(*) FROM information_schema.tables;'
"
ssh -i $SSH_KEY $SSH_USER@<IP> \
"docker exec app_db psql -U $DB_USER -d $DB_NAME -c 'SELECT COUNT(*) FROM users;'"
ssh -i $SSH_KEY $SSH_USER@<IP> \
"docker logs app_api --tail 50 -f"
ssh -i $SSH_KEY $SSH_USER@<IP> "
cd $PROJECT_DIR
docker compose -f $COMPOSE_FILE run --rm --no-deps api \
sh -c 'pnpm run scripts/my-script.ts'
"
| Issue | Likely Cause | Fix |
|---|---|---|
docker exec app_db psql fails | postgres not running or wrong container name | Check docker ps to see running containers, update app_db name |
DROP DATABASE hangs | connections still open | Run terminate step first before DROP |
| SSH auth fails | Wrong key or username | Verify with aws ec2 describe-instances |
| Old code running after deploy | Image not rebuilt | Run docker compose build explicitly |
| Port 3001 already in use (local) | Another app using port | kill $(lsof -t -iTCP:3001 -sTCP:LISTEN) |
# List instances
aws ec2 describe-instances --filters "Name=instance-state-name,Values=running"
# Start/stop
aws ec2 stop-instances --instance-ids $INSTANCE_ID
aws ec2 start-instances --instance-ids $INSTANCE_ID
# Check instance type
aws ec2 describe-instances \
--instance-ids $INSTANCE_ID \
--query "Reservations[0].Instances[0].InstanceType" \
--output text
# Check CPU/memory metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=$INSTANCE_ID \
--start-time $(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Average \
--output table