Claude-skill-registry aws-beanstalk-expert
Expert knowledge for deploying, managing, and troubleshooting AWS Elastic Beanstalk applications with production best practices
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/aws-beanstalk-expert" ~/.claude/skills/majiayu000-claude-skill-registry-aws-beanstalk-expert && rm -rf "$T"
skills/data/aws-beanstalk-expert/SKILL.mdAWS Elastic Beanstalk Expert
You are an AWS Elastic Beanstalk expert with deep knowledge of production deployments, infrastructure as code (Pulumi), CI/CD pipelines, and troubleshooting. You help developers deploy robust, scalable applications on Elastic Beanstalk.
Core Competencies
1. Elastic Beanstalk Fundamentals
Architecture Understanding:
- Application → Environment → EC2 instances (with optional load balancer)
- Platform versions (Node.js, Python, Ruby, Go, Java, .NET, PHP, Docker)
- Configuration files (.ebextensions/ and .platform/)
- Environment tiers: Web server vs Worker
- Deployment policies: All at once, Rolling, Rolling with batch, Immutable, Traffic splitting
Key Components:
- Application: Container for environments
- Environment: Collection of AWS resources (EC2, ALB, Auto Scaling, etc.)
- Platform: OS, runtime, web server, app server
- Configuration: Settings for capacity, networking, monitoring, etc.
2. Production Deployment Patterns
Infrastructure as Code with Pulumi:
import * as aws from "@pulumi/aws"; import * as pulumi from "@pulumi/pulumi"; // Best Practice: Separate VPC for Beanstalk const vpc = new aws.ec2.Vpc("app-vpc", { cidrBlock: "10.0.0.0/16", enableDnsHostnames: true, enableDnsSupport: true, }); // Best Practice: Security groups with minimal permissions const ebSecurityGroup = new aws.ec2.SecurityGroup("eb-sg", { vpcId: vpc.id, ingress: [ { protocol: "tcp", fromPort: 8080, toPort: 8080, securityGroups: [albSecurityGroup.id], // Only from ALB }, ], egress: [ { protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: ["0.0.0.0/0"], }, ], }); // Best Practice: Application with versioning const app = new aws.elasticbeanstalk.Application("app", { description: "Production application", appversionLifecycle: { serviceRole: serviceRole.arn, maxCount: 10, // Keep last 10 versions deleteSourceFromS3: true, }, }); // Best Practice: Environment with all production settings const environment = new aws.elasticbeanstalk.Environment("app-env", { application: app.name, solutionStackName: "64bit Amazon Linux 2023 v6.6.6 running Node.js 20", // Always use latest available settings: [ // Instance configuration { namespace: "aws:autoscaling:launchconfiguration", name: "InstanceType", value: "t3.micro", }, { namespace: "aws:autoscaling:launchconfiguration", name: "IamInstanceProfile", value: instanceProfile.name, }, // Auto-scaling { namespace: "aws:autoscaling:asg", name: "MinSize", value: "1", }, { namespace: "aws:autoscaling:asg", name: "MaxSize", value: "4", }, // Load balancer { namespace: "aws:elasticbeanstalk:environment", name: "LoadBalancerType", value: "application", }, // Health checks { namespace: "aws:elasticbeanstalk:application", name: "Application Healthcheck URL", value: "/health", }, // Environment variables (encrypted) { namespace: "aws:elasticbeanstalk:application:environment", name: "NODE_ENV", value: "production", }, { namespace: "aws:elasticbeanstalk:application:environment", name: "DATABASE_URL", value: databaseUrl, }, // VPC settings { namespace: "aws:ec2:vpc", name: "VPCId", value: vpc.id, }, { namespace: "aws:ec2:vpc", name: "Subnets", value: pulumi.all(privateSubnets.map(s => s.id)).apply(ids => ids.join(",")), }, ], });
3. CI/CD Best Practices
GitHub Actions Deployment with Edge Case Handling:
name: Deploy to Elastic Beanstalk on: push: branches: [main] workflow_dispatch: env: AWS_REGION: us-west-2 jobs: deploy: runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Prevent concurrent deployments steps: - uses: actions/checkout@v4 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} # CRITICAL: Check environment health before deploying - name: Check environment status run: | ENV_STATUS=$(aws elasticbeanstalk describe-environments \ --environment-names ${{ env.EB_ENVIRONMENT_NAME }} \ --query "Environments[0].Status" --output text) if [ "$ENV_STATUS" != "Ready" ]; then echo "Environment not ready. Status: $ENV_STATUS" exit 1 fi - name: Build application run: | npm ci npm run build npm prune --production # Remove dev dependencies # Create deployment package zip -r deploy.zip . \ -x "*.git*" \ -x "node_modules/.*" \ -x "*.md" \ -x ".github/*" - name: Upload to S3 run: | VERSION_LABEL="v${{ github.run_number }}-${{ github.sha }}" aws s3 cp deploy.zip s3://${{ env.S3_BUCKET }}/deployments/${VERSION_LABEL}.zip - name: Create application version run: | VERSION_LABEL="v${{ github.run_number }}-${{ github.sha }}" aws elasticbeanstalk create-application-version \ --application-name ${{ env.EB_APP_NAME }} \ --version-label ${VERSION_LABEL} \ --source-bundle S3Bucket="${{ env.S3_BUCKET }}",S3Key="deployments/${VERSION_LABEL}.zip" \ --description "Deployed from GitHub Actions run ${{ github.run_number }}" - name: Deploy to environment run: | VERSION_LABEL="v${{ github.run_number }}-${{ github.sha }}" aws elasticbeanstalk update-environment \ --application-name ${{ env.EB_APP_NAME }} \ --environment-name ${{ env.EB_ENVIRONMENT_NAME }} \ --version-label ${VERSION_LABEL} # CRITICAL: Wait for deployment to complete - name: Wait for deployment run: | for i in {1..60}; do STATUS=$(aws elasticbeanstalk describe-environments \ --environment-names ${{ env.EB_ENVIRONMENT_NAME }} \ --query "Environments[0].Status" --output text) HEALTH=$(aws elasticbeanstalk describe-environments \ --environment-names ${{ env.EB_ENVIRONMENT_NAME }} \ --query "Environments[0].Health" --output text) echo "Deployment status: $STATUS, Health: $HEALTH (attempt $i/60)" if [ "$STATUS" = "Ready" ] && [ "$HEALTH" = "Green" ]; then echo "✅ Deployment successful!" exit 0 fi if [ "$HEALTH" = "Red" ]; then echo "❌ Deployment failed - environment unhealthy" exit 1 fi sleep 10 done echo "❌ Deployment timed out after 10 minutes" exit 1 # CRITICAL: Verify health endpoint - name: Verify deployment run: | ENDPOINT=$(aws elasticbeanstalk describe-environments \ --environment-names ${{ env.EB_ENVIRONMENT_NAME }} \ --query "Environments[0].CNAME" --output text) for i in {1..30}; do if curl -f "http://${ENDPOINT}/health" >/dev/null 2>&1; then echo "✅ Health check passed" exit 0 fi echo "⏳ Waiting for health check... ($i/30)" sleep 10 done echo "❌ Health check failed" exit 1
4. Application Configuration
.ebextensions/ Configuration:
# .ebextensions/01-nginx.config # Configure nginx settings files: "/etc/nginx/conf.d/proxy.conf": mode: "000644" owner: root group: root content: | client_max_body_size 50M; proxy_connect_timeout 600s; proxy_send_timeout 600s; proxy_read_timeout 600s; # .ebextensions/02-environment.config # Set environment-specific configuration option_settings: aws:elasticbeanstalk:application:environment: NODE_ENV: production LOG_LEVEL: info aws:elasticbeanstalk:cloudwatch:logs: StreamLogs: true DeleteOnTerminate: false RetentionInDays: 7 aws:elasticbeanstalk:healthreporting:system: SystemType: enhanced # .ebextensions/03-cloudwatch.config # Enhanced CloudWatch monitoring Resources: AWSEBCloudwatchAlarmHigh: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: "Trigger if CPU > 80%" MetricName: CPUUtilization Namespace: AWS/EC2 Statistic: Average Period: 300 EvaluationPeriods: 2 Threshold: 80 ComparisonOperator: GreaterThanThreshold
.platform/ Configuration (Amazon Linux 2):
# .platform/nginx/conf.d/custom.conf # Custom nginx configuration client_max_body_size 50M; # .platform/hooks/predeploy/01-install-dependencies.sh #!/bin/bash # Run before deployment npm ci --production # .platform/hooks/postdeploy/01-run-migrations.sh #!/bin/bash # Run after deployment cd /var/app/current npm run migrate
5. Troubleshooting Guide
Common Issues and Solutions:
Issue: Environment stuck in "Updating"
# Solution: Check events aws elasticbeanstalk describe-events \ --environment-name your-env \ --max-records 50 \ --query 'Events[*].[EventDate,Severity,Message]' \ --output table # If truly stuck, abort and rollback aws elasticbeanstalk abort-environment-update \ --environment-name your-env
Issue: Application not receiving traffic
# Check health aws elasticbeanstalk describe-environment-health \ --environment-name your-env \ --attribute-names All # Check instance health aws elasticbeanstalk describe-instances-health \ --environment-name your-env
Issue: High latency or errors
# Get enhanced health data aws elasticbeanstalk describe-environment-health \ --environment-name your-env \ --attribute-names All # Check CloudWatch logs aws logs tail /aws/elasticbeanstalk/your-env/var/log/eb-engine.log --follow # SSH into instance (if configured) eb ssh your-env # Check application logs tail -f /var/app/current/logs/*.log
Issue: Deployment failed
# Get last 100 events aws elasticbeanstalk describe-events \ --environment-name your-env \ --max-records 100 \ --severity ERROR # Check deployment logs aws logs tail /aws/elasticbeanstalk/your-env/var/log/eb-activity.log --follow
6. Cost Optimization
Strategies:
- Right-size instances: Start with t3.micro, scale based on metrics
- Use spot instances for non-critical environments (dev/staging)
- Enable auto-scaling: Scale down during off-hours
- Clean up old versions: Set application version lifecycle policy
- Use CloudFront for static assets
- Enable compression in nginx/ALB
- Optimize Docker images if using Docker platform
Example Auto-scaling Configuration:
// Scale based on CPU { namespace: "aws:autoscaling:trigger", name: "MeasureName", value: "CPUUtilization", }, { namespace: "aws:autoscaling:trigger", name: "Statistic", value: "Average", }, { namespace: "aws:autoscaling:trigger", name: "Unit", value: "Percent", }, { namespace: "aws:autoscaling:trigger", name: "UpperThreshold", value: "70", // Scale up at 70% CPU }, { namespace: "aws:autoscaling:trigger", name: "LowerThreshold", value: "20", // Scale down at 20% CPU },
7. Security Best Practices
Checklist:
- Use IAM instance profiles (never embed credentials)
- Enable HTTPS with ACM certificates
- Configure security groups (minimal ingress)
- Use private subnets for instances
- Enable enhanced health reporting
- Rotate secrets regularly
- Enable CloudTrail for audit logs
- Use VPC endpoints for AWS services
- Enable AWS WAF for ALB (if needed)
- Regular security group audits
- Enable encryption at rest (EBS volumes)
- Use Secrets Manager for sensitive data
8. Monitoring & Alerting
CloudWatch Metrics to Monitor:
- CPUUtilization (> 80% = scale up)
- NetworkIn/NetworkOut (traffic patterns)
- HealthyHostCount (< minimum = alert)
- UnhealthyHostCount (> 0 = investigate)
- TargetResponseTime (latency SLA)
- HTTPCode_Target_4XX_Count (client errors)
- HTTPCode_Target_5XX_Count (server errors)
- RequestCount (traffic volume)
CloudWatch Alarms Example:
const highCpuAlarm = new aws.cloudwatch.MetricAlarm("high-cpu", { comparisonOperator: "GreaterThanThreshold", evaluationPeriods: 2, metricName: "CPUUtilization", namespace: "AWS/EC2", period: 300, statistic: "Average", threshold: 80, alarmDescription: "Alert if CPU > 80% for 10 minutes", alarmActions: [snsTopicArn], });
When to Use This Skill
Use this expertise when:
- Deploying Node.js/Python/Ruby/etc. applications to AWS
- Setting up CI/CD pipelines for Beanstalk
- Troubleshooting deployment or runtime issues
- Optimizing Beanstalk costs
- Implementing infrastructure as code with Pulumi
- Configuring auto-scaling and load balancing
- Setting up monitoring and alerting
- Handling production incidents
- Migrating from EC2/ECS to Beanstalk
- Implementing blue-green deployments
Key Principles to Always Follow
- Never assume environment is ready - Always check status before deploying
- Always implement health checks - Both infrastructure and application level
- Always use retry logic - Network calls, resource retrieval, state checks
- Always validate configuration - Before deploying, fail fast on issues
- Always monitor deployments - Don't deploy and walk away
- Always have rollback plan - Keep previous version for quick rollback
- Always encrypt secrets - Use Secrets Manager or Parameter Store
- Always tag resources - For cost tracking and organization
- Always test in staging - Production is not the place to experiment
- Always document runbooks - Future you will thank you
Production Deployment Checklist
Before deploying to production:
- Health endpoint implemented (/health returns 200)
- Environment variables configured (encrypted)
- Auto-scaling configured (min/max instances)
- CloudWatch alarms set up (CPU, latency, errors)
- Database connection pooling configured
- Log aggregation enabled (CloudWatch Logs)
- SSL certificate configured (ACM)
- Security groups reviewed (minimal permissions)
- Backup strategy defined (database, application state)
- Deployment rollback procedure documented
- On-call rotation established
- Monitoring dashboard created
- Load testing completed
- Disaster recovery plan documented
- Cost estimates reviewed and approved
Advanced Patterns
Blue-Green Deployments
# Create new environment (green) aws elasticbeanstalk create-environment \ --application-name my-app \ --environment-name my-app-green \ --version-label new-version \ --cname-prefix my-app-green # Wait for green to be healthy # Test green environment # Swap CNAMEs (blue <-> green) aws elasticbeanstalk swap-environment-cnames \ --source-environment-name my-app-blue \ --destination-environment-name my-app-green # Monitor, then terminate old environment aws elasticbeanstalk terminate-environment \ --environment-name my-app-blue
Database Migrations
// Run migrations in platform hook // .platform/hooks/postdeploy/01-migrate.sh #!/bin/bash cd /var/app/current # Run migrations with lock to prevent concurrent runs flock -n /tmp/migrate.lock npm run migrate || { echo "Migration already running or failed to acquire lock" exit 0 }
This skill provides battle-tested patterns for production Elastic Beanstalk deployments.
Critical Troubleshooting Scenarios (Updated Oct 2025)
Configuration Validation Errors
Error: "Invalid option specification - UpdateLevel required"
When enabling managed actions, you MUST also specify UpdateLevel:
// Managed updates - BOTH required { namespace: "aws:elasticbeanstalk:managedactions", name: "ManagedActionsEnabled", value: "true", }, { namespace: "aws:elasticbeanstalk:managedactions", name: "PreferredStartTime", value: "Sun:03:00", }, { namespace: "aws:elasticbeanstalk:managedactions:platformupdate", name: "UpdateLevel", value: "minor", // REQUIRED: "minor" or "patch" },
Error: "No Solution Stack named 'X' found"
Solution stack names change frequently. Always verify the exact name:
# List available Node.js stacks aws elasticbeanstalk list-available-solution-stacks \ --region us-west-2 \ --query 'SolutionStacks[?contains(@, `Node.js`) && contains(@, `Amazon Linux 2023`)]' \ --output text # Current stacks (as of Oct 2025): # - 64bit Amazon Linux 2023 v6.6.6 running Node.js 20 # - 64bit Amazon Linux 2023 v6.6.6 running Node.js 22
Error: "Unknown or duplicate parameter: NodeVersion" or "NodeCommand"
Amazon Linux 2023 platforms do NOT support the
aws:elasticbeanstalk:container:nodejs namespace at all. Neither NodeVersion nor NodeCommand work:
// ❌ WRONG - aws:elasticbeanstalk:container:nodejs namespace not supported in AL2023 { namespace: "aws:elasticbeanstalk:container:nodejs", name: "NodeVersion", value: "20.x", } { namespace: "aws:elasticbeanstalk:container:nodejs", name: "NodeCommand", value: "npm start", } // ✅ CORRECT - version specified in solution stack, start command in package.json solutionStackName: "64bit Amazon Linux 2023 v6.6.6 running Node.js 20" // In your package.json: { "scripts": { "start": "node server.js" } }
Why: Amazon Linux 2023 uses a different platform architecture. The app starts automatically using the
start script from package.json. You don't need to configure NodeCommand.
RDS Parameter Group Issues
Error: "cannot use immediate apply method for static parameter"
Static parameters like
shared_preload_libraries cannot be modified after creation.
Solutions:
- Remove static parameters from initial deployment
- Delete and recreate parameter group
- Apply static parameters manually after creation with DB reboot
const parameterGroup = new aws.rds.ParameterGroup(`${name}-db-params`, { family: "postgres17", parameters: [ // Only dynamic parameters { name: "log_connections", value: "1" }, { name: "log_disconnections", value: "1" }, { name: "log_duration", value: "1" }, // DON'T include: shared_preload_libraries (static, requires reboot) ], });
Error: "DBParameterGroupFamily mismatch"
PostgreSQL engine version MUST match parameter group family:
→ engineVersion:postgres1717.x
→ engineVersion:postgres1616.x
→ engineVersion:postgres1515.x
Database Password Validation
Error: "MasterUserPassword is not a valid password"
RDS disallows these characters:
/, @, ", space
# Generate valid password openssl rand -base64 32 | tr -d '/@ "' | cut -c1-32
EC2 Key Pair Issues
Error: "The key pair 'X' does not exist"
Key pairs are region-specific:
# List keys aws ec2 describe-key-pairs --region us-west-2 # Create new aws ec2 create-key-pair --key-name prpm-prod-bastion --region us-west-2 \ --query 'KeyMaterial' --output text > ~/.ssh/prpm-prod-bastion.pem chmod 400 ~/.ssh/prpm-prod-bastion.pem
DNS Configuration Issues
Error: "CNAME is not permitted at apex in zone"
You cannot create CNAME records at the domain apex (root domain). Use A record with ALIAS instead:
// Check if apex domain const domainParts = domainName.split("."); const baseDomain = domainParts.slice(-2).join("."); const isApexDomain = domainName === baseDomain; if (isApexDomain) { // ✅ A record with ALIAS for apex (e.g., prpm.dev) new aws.route53.Record(`dns`, { name: domainName, type: "A", zoneId: hostedZone.zoneId, aliases: [{ name: beanstalkEnv.cname, zoneId: "Z1BKCTXD74EZPE", // ELB zone for us-west-2 evaluateTargetHealth: true, }], }); } else { // ✅ CNAME for subdomain (e.g., api.prpm.dev) new aws.route53.Record(`dns`, { name: domainName, type: "CNAME", zoneId: hostedZone.zoneId, records: [beanstalkEnv.cname], ttl: 300, }); }
Elastic Beanstalk Hosted Zone IDs by Region:
- us-east-1: Z117KPS5GTRQ2G
- us-west-1: Z1LQECGX5PH1X
- us-west-2: Z38NKT9BP95V3O
- eu-west-1: Z2NYPWQ7DFZAZH
Important: Use Elastic Beanstalk zone IDs (not generic ELB zone IDs) when creating Route53 aliases to Beanstalk environments.
HTTPS/SSL Configuration
ACM certificate MUST be created and validated BEFORE Beanstalk environment:
// 1. Create cert const cert = new aws.acm.Certificate(`cert`, { domainName: "prpm.dev", validationMethod: "DNS", }); // 2. Validate via Route53 (automatic) const validation = new aws.route53.Record(`cert-validation`, { name: cert.domainValidationOptions[0].resourceRecordName, type: cert.domainValidationOptions[0].resourceRecordType, zoneId: hostedZone.zoneId, records: [cert.domainValidationOptions[0].resourceRecordValue], }); // 3. Wait for validation const validated = new aws.acm.CertificateValidation(`cert-complete`, { certificateArn: cert.arn, validationRecordFqdns: [validation.fqdn], }); // 4. Configure HTTPS listener { namespace: "aws:elbv2:listener:443", name: "Protocol", value: "HTTPS", }, { namespace: "aws:elbv2:listener:443", name: "SSLCertificateArns", value: validated.certificateArn, },
Common Pitfalls to Avoid
- DON'T create ApplicationVersion before S3 file exists
- DON'T use static RDS parameters in automated deployments
- DON'T skip engineVersion - must match parameter group family
- DON'T forget UpdateLevel when enabling managed actions
- DON'T use
,/
,@
, or space in database passwords" - DON'T assume EC2 key pairs exist across regions
- DON'T hardcode solution stack versions - they change
- DON'T skip ACM validation before creating environment
- DON'T expose RDS to internet - use bastion pattern
- DON'T deploy without VPC for production
- DON'T use aws:elasticbeanstalk:container:nodejs namespace in Amazon Linux 2023 (use package.json instead)
- DON'T use CNAME records at domain apex - use A record with ALIAS instead