Claude Code Plugins

Community-maintained marketplace

Feedback

AWS Elastic Beanstalk Expert

@pr-pm/prpm
7
0

Expert knowledge for deploying, managing, and troubleshooting AWS Elastic Beanstalk applications with production best practices

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name AWS Elastic Beanstalk Expert
description Expert knowledge for deploying, managing, and troubleshooting AWS Elastic Beanstalk applications with production best practices
author PRPM Team
version 1.0.0
tags aws, elastic-beanstalk, deployment, infrastructure, devops, pulumi, ci-cd

AWS 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:

  1. Right-size instances: Start with t3.micro, scale based on metrics
  2. Use spot instances for non-critical environments (dev/staging)
  3. Enable auto-scaling: Scale down during off-hours
  4. Clean up old versions: Set application version lifecycle policy
  5. Use CloudFront for static assets
  6. Enable compression in nginx/ALB
  7. 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

  1. Never assume environment is ready - Always check status before deploying
  2. Always implement health checks - Both infrastructure and application level
  3. Always use retry logic - Network calls, resource retrieval, state checks
  4. Always validate configuration - Before deploying, fail fast on issues
  5. Always monitor deployments - Don't deploy and walk away
  6. Always have rollback plan - Keep previous version for quick rollback
  7. Always encrypt secrets - Use Secrets Manager or Parameter Store
  8. Always tag resources - For cost tracking and organization
  9. Always test in staging - Production is not the place to experiment
  10. 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:

  1. Remove static parameters from initial deployment
  2. Delete and recreate parameter group
  3. 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:

  • postgres17 → engineVersion: 17.x
  • postgres16 → engineVersion: 16.x
  • postgres15 → engineVersion: 15.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.

Full list

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

  1. DON'T create ApplicationVersion before S3 file exists
  2. DON'T use static RDS parameters in automated deployments
  3. DON'T skip engineVersion - must match parameter group family
  4. DON'T forget UpdateLevel when enabling managed actions
  5. DON'T use /, @, ", or space in database passwords
  6. DON'T assume EC2 key pairs exist across regions
  7. DON'T hardcode solution stack versions - they change
  8. DON'T skip ACM validation before creating environment
  9. DON'T expose RDS to internet - use bastion pattern
  10. DON'T deploy without VPC for production
  11. DON'T use aws:elasticbeanstalk:container:nodejs namespace in Amazon Linux 2023 (use package.json instead)
  12. DON'T use CNAME records at domain apex - use A record with ALIAS instead