Elastic Beanstalk for Quick Deployments
Deploy Node.js applications to AWS Elastic Beanstalk with custom configurations, rolling deployments, and auto-scaling
Elastic Beanstalk for Quick Deployments
Overview
AWS Elastic Beanstalk is a platform-as-a-service (PaaS) that handles provisioning, load balancing, scaling, and monitoring so you can focus on writing code instead of managing infrastructure. It sits on top of core AWS services like EC2, ELB, Auto Scaling, and CloudWatch, giving you full access to those underlying resources while abstracting away the operational complexity. If you need to get a Node.js application into production on AWS without spending weeks learning CloudFormation or Terraform, Elastic Beanstalk is the fastest path from your terminal to a running, scalable deployment.
Prerequisites
Before you start, make sure you have the following in place:
- An AWS account with IAM credentials configured
- Node.js v18 or later installed locally
- The AWS CLI installed and configured (
aws configure) - The EB CLI installed (
pip install awsebcli) - A basic understanding of Express.js and npm
- Git installed (EB CLI uses git for deployments by default)
What Elastic Beanstalk Provides
Elastic Beanstalk is not a separate compute service. It is an orchestration layer. When you deploy an application, Beanstalk provisions real EC2 instances, creates an Elastic Load Balancer, sets up an Auto Scaling group, configures CloudWatch alarms, and wires everything together. You get a fully managed environment that you can still SSH into, customize with configuration files, and tune at every level.
The key resources Beanstalk manages for you:
- EC2 instances running your application code
- Elastic Load Balancer (ALB or Classic) distributing traffic
- Auto Scaling group handling capacity changes
- Security groups controlling network access
- CloudWatch alarms monitoring health and performance
- S3 bucket storing application versions
- RDS instance (optional, can be attached or decoupled)
The important distinction from something like Lambda or Fargate is that you still have EC2 instances. You can SSH into them, install packages, inspect logs, and debug in ways that serverless platforms make difficult. But you do not have to manage AMIs, launch configurations, or deployment scripts yourself.
Platform Options for Node.js
Elastic Beanstalk supports multiple Node.js platform versions. When you create an environment, you select a platform branch. As of early 2026, the relevant options are:
| Platform Branch | Node.js Version | Amazon Linux |
|---|---|---|
| Node.js 20 | 20.x | Amazon Linux 2023 |
| Node.js 18 | 18.x | Amazon Linux 2023 |
Always use the latest Amazon Linux 2023-based platforms. The older Amazon Linux 2 platforms are being phased out. You can specify the platform explicitly:
eb create my-env --platform "Node.js 20 running on 64bit Amazon Linux 2023"
Or let the EB CLI prompt you during eb init.
EB CLI Setup and Usage
The EB CLI is the primary tool for working with Elastic Beanstalk. Install it with pip:
pip install awsebcli --upgrade
Verify the installation:
eb --version
The CLI commands you will use most often:
# Initialize a new EB application in your project directory
eb init
# Create an environment and deploy
eb create my-environment
# Deploy code changes to the running environment
eb deploy
# Open the application in a browser
eb open
# View recent logs
eb logs
# SSH into an instance
eb ssh
# View environment status
eb status
# Set environment variables
eb setenv KEY1=value1 KEY2=value2
# Terminate an environment (destroys all resources)
eb terminate my-environment
Initializing a Project
Run eb init in your project root. The CLI walks you through selecting a region, application name, and platform. It creates a .elasticbeanstalk/config.yml file:
branch-defaults:
master:
environment: my-app-prod
global:
application_name: my-app
default_platform: Node.js 20 running on 64bit Amazon Linux 2023
default_region: us-west-2
workspace_type: Application
Add .elasticbeanstalk/ to your .gitignore since it contains local configuration.
Application and Environment Creation
Elastic Beanstalk has two core concepts: applications and environments. An application is a logical container. An environment is a running instance of your code with specific configuration. You typically have multiple environments per application (dev, staging, production).
# Create a production environment with a load balancer
eb create prod-env --instance-type t3.small --scale 2
# Create a single-instance dev environment (no load balancer, cheaper)
eb create dev-env --single
The --single flag is important for development. It creates a single EC2 instance without a load balancer, which saves cost and simplifies debugging.
Customization with .ebextensions
The .ebextensions directory is where Elastic Beanstalk gets powerful. You place YAML configuration files (with a .config extension) in this directory, and Beanstalk processes them during deployment. The files are processed in alphabetical order, so prefix them with numbers.
Create the directory in your project root:
mkdir .ebextensions
Installing Packages
# .ebextensions/01-packages.config
packages:
yum:
git: []
ImageMagick: []
Running Commands During Deployment
# .ebextensions/02-commands.config
commands:
01_create_temp_dir:
command: "mkdir -p /tmp/myapp/cache"
02_set_permissions:
command: "chmod 755 /tmp/myapp/cache"
container_commands:
01_run_migrations:
command: "node scripts/migrate.js"
leader_only: true
The distinction between commands and container_commands matters. commands run before the application code is extracted. container_commands run after extraction but before the application starts. The leader_only flag ensures the command runs on only one instance during scaling events, which is critical for database migrations.
Custom Nginx Configuration
Elastic Beanstalk uses Nginx as a reverse proxy on Amazon Linux 2023 platforms. You can extend its configuration:
# .ebextensions/03-nginx.config
files:
"/etc/nginx/conf.d/proxy.conf":
mode: "000644"
owner: root
group: root
content: |
client_max_body_size 50M;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
Custom Log Files
# .ebextensions/04-logs.config
files:
"/opt/elasticbeanstalk/tasks/taillogs.d/app-logs.conf":
mode: "000644"
owner: root
group: root
content: |
/var/app/current/logs/*.log
This tells Beanstalk to include your custom log files when you run eb logs.
Environment Variables and Configuration
Never hardcode secrets or configuration values. Use environment variables:
# Set variables via CLI
eb setenv NODE_ENV=production DB_HOST=mydb.cluster.us-west-2.rds.amazonaws.com DB_PASSWORD=secret123 PORT=8080
You can also set them in .ebextensions:
# .ebextensions/05-env.config
option_settings:
aws:elasticbeanstalk:application:environment:
NODE_ENV: production
PORT: 8080
Do not put secrets in .ebextensions files since those get committed to source control. Use eb setenv for sensitive values or pull them from AWS Secrets Manager at runtime.
In your Node.js application, access them normally:
var port = process.env.PORT || 8080;
var dbHost = process.env.DB_HOST;
var nodeEnv = process.env.NODE_ENV;
Elastic Beanstalk sets PORT to 8080 by default and expects your application to listen on that port. Do not change this unless you also update the Nginx proxy configuration.
Deployment Policies
Elastic Beanstalk supports several deployment strategies. Choosing the right one depends on your tolerance for downtime and the size of your fleet.
All at Once
Deploys to all instances simultaneously. Fast but causes downtime.
# .ebextensions/06-deployment.config
option_settings:
aws:elasticbeanstalk:command:
DeploymentPolicy: AllAtOnce
Use this for development environments where downtime does not matter.
Rolling
Deploys to batches of instances. The remaining instances continue serving traffic.
option_settings:
aws:elasticbeanstalk:command:
DeploymentPolicy: Rolling
BatchSize: 30
BatchSizeType: Percentage
This deploys to 30% of instances at a time. During deployment, your fleet runs a mix of old and new code. Your application must handle this gracefully.
Rolling with Additional Batch
Like rolling, but launches a fresh batch of instances first so you never drop below full capacity.
option_settings:
aws:elasticbeanstalk:command:
DeploymentPolicy: RollingWithAdditionalBatch
BatchSize: 1
BatchSizeType: Fixed
Immutable
Launches an entirely new set of instances with the new code, health-checks them, then swaps them into the Auto Scaling group and terminates the old instances.
option_settings:
aws:elasticbeanstalk:command:
DeploymentPolicy: Immutable
This is the safest option. If the new instances fail health checks, Beanstalk rolls back automatically. It is slower and costs more (temporarily doubles your fleet), but it is what I use for production.
Blue-Green Deployment
Blue-green is not a built-in deployment policy. You achieve it by creating a second environment and swapping CNAMEs:
# Create a "green" environment
eb create prod-green --cfg saved-prod-config
# Deploy new version to green
eb deploy prod-green
# Test green environment
curl https://prod-green.us-west-2.elasticbeanstalk.com/health
# Swap URLs between blue and green
eb swap prod-blue --destination_name prod-green
The CNAME swap is nearly instantaneous and gives you the safest rollback path. If something goes wrong, swap back.
Procfile and Platform Hooks
Procfile
The Procfile tells Beanstalk how to start your application. Create it in your project root:
web: node app.js
Without a Procfile, Beanstalk runs npm start. I prefer being explicit with a Procfile because it removes ambiguity and starts the process directly without npm overhead.
You can also run background workers:
web: node app.js
worker: node worker.js
Platform Hooks
Platform hooks run scripts at specific points in the deployment lifecycle. They are more powerful than .ebextensions commands for complex scenarios.
# Create hook directories
mkdir -p .platform/hooks/predeploy
mkdir -p .platform/hooks/postdeploy
#!/bin/bash
# .platform/hooks/predeploy/01_install_deps.sh
cd /var/app/staging
npm ci --production
#!/bin/bash
# .platform/hooks/postdeploy/01_run_migrations.sh
cd /var/app/current
node scripts/migrate.js
Make sure hook scripts are executable:
chmod +x .platform/hooks/predeploy/01_install_deps.sh
chmod +x .platform/hooks/postdeploy/01_run_migrations.sh
Log Access and Monitoring
Viewing Logs
# Request last 100 lines from all instances
eb logs
# Request full logs bundled as a zip
eb logs --all
# Stream logs in real-time
eb logs --stream
Logs are also stored in CloudWatch if you enable it:
# .ebextensions/07-cloudwatch.config
option_settings:
aws:elasticbeanstalk:cloudwatch:logs:
StreamLogs: true
DeleteOnTerminate: false
RetentionInDays: 30
Health Monitoring
Beanstalk checks the health of your application by making HTTP requests to the root path (/) by default. Configure a dedicated health endpoint:
option_settings:
aws:elasticbeanstalk:application:
Application Healthcheck URL: /health
In your Express app:
app.get("/health", function(req, res) {
res.status(200).json({
status: "healthy",
uptime: process.uptime(),
timestamp: new Date().toISOString()
});
});
Enable enhanced health reporting for more detailed metrics:
option_settings:
aws:elasticbeanstalk:healthreporting:system:
SystemType: enhanced
Scaling Configuration
Auto Scaling is where Elastic Beanstalk earns its keep. Configure scaling based on CPU, network, or request count:
# .ebextensions/08-scaling.config
option_settings:
aws:autoscaling:asg:
MinSize: 2
MaxSize: 8
aws:autoscaling:trigger:
MeasureName: CPUUtilization
Statistic: Average
Unit: Percent
UpperThreshold: 70
LowerThreshold: 30
UpperBreachScaleIncrement: 2
LowerBreachScaleIncrement: -1
BreachDuration: 5
Period: 5
aws:autoscaling:launchconfiguration:
InstanceType: t3.small
SecurityGroups: sg-xxxxxxxx
This configuration scales up by 2 instances when average CPU exceeds 70% for 5 minutes, and scales down by 1 instance when CPU drops below 30%.
For production workloads, I recommend a minimum of 2 instances across different Availability Zones for high availability.
Load Balancer Setup
By default, Beanstalk creates an Application Load Balancer (ALB). Configure it for your needs:
# .ebextensions/09-alb.config
option_settings:
aws:elasticbeanstalk:environment:
LoadBalancerType: application
aws:elbv2:listener:443:
ListenerEnabled: true
Protocol: HTTPS
SSLCertificateArns: arn:aws:acm:us-west-2:123456789:certificate/abc-123
aws:elbv2:listener:default:
ListenerEnabled: true
Protocol: HTTP
aws:elbv2:listener:80:
DefaultProcess: default
ListenerEnabled: true
Protocol: HTTP
Rules: redirect-http
aws:elbv2:listenerrule:redirect-http:
PathPatterns: /*
Process: default
Priority: 1
Sticky Sessions
If your application uses sessions, enable sticky sessions on the load balancer:
option_settings:
aws:elasticbeanstalk:environment:process:default:
StickinessEnabled: true
StickinessLBCookieDuration: 86400
A better approach is to use Redis or DynamoDB for session storage so your application is stateless and can scale freely.
Custom Domain and SSL
SSL Certificate
Request a free SSL certificate through AWS Certificate Manager (ACM):
aws acm request-certificate \
--domain-name myapp.example.com \
--validation-method DNS \
--region us-west-2
Validate the certificate via DNS, then reference it in your load balancer configuration as shown above.
Custom Domain
Point your domain to the Elastic Beanstalk environment using a CNAME record or, if using Route 53, an alias record:
aws route53 change-resource-record-sets \
--hosted-zone-id Z1234567890 \
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "myapp.example.com",
"Type": "CNAME",
"TTL": 300,
"ResourceRecords": [{"Value": "my-env.us-west-2.elasticbeanstalk.com"}]
}
}]
}'
HTTPS Redirect
Force all HTTP traffic to HTTPS with an Nginx configuration extension:
# .ebextensions/10-https-redirect.config
files:
"/etc/nginx/conf.d/https_redirect.conf":
mode: "000644"
owner: root
group: root
content: |
server {
listen 80;
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
}
Note the use of $http_x_forwarded_proto instead of $scheme. Since Nginx sits behind the load balancer, the connection between the ALB and Nginx is always HTTP. You need the forwarded header to determine the original protocol.
Docker on Elastic Beanstalk
Elastic Beanstalk supports Docker deployments, which gives you full control over the runtime environment. This is useful when you need specific system libraries, a particular Node.js version, or want to keep your deployment environment identical to local development.
Create a Dockerfile in your project root:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 8080
CMD ["node", "app.js"]
Beanstalk detects the Dockerfile and builds the image on the instance. For faster deployments, push your image to ECR and reference it:
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "123456789.dkr.ecr.us-west-2.amazonaws.com/my-app:latest",
"Update": "true"
},
"Ports": [
{
"ContainerPort": 8080,
"HostPort": 8080
}
]
}
Save this as Dockerrun.aws.json in your project root.
Migration Path to ECS
Elastic Beanstalk is a great starting point, but as your application grows you may want to migrate to ECS (Elastic Container Service) for more control over container orchestration. The migration path looks like this:
- Start with Beanstalk Docker platform - This gets you containerized early
- Move to ECR - Push images to Elastic Container Registry instead of building on instances
- Create an ECS task definition from your Dockerfile
- Set up an ECS service with an ALB (you can reuse your existing one)
- Shift traffic gradually using weighted target groups
The Docker-first approach on Beanstalk makes this transition smoother because your application is already containerized. The main work is defining the ECS task definition, service, and cluster configuration.
# Push your Docker image to ECR
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-west-2.amazonaws.com
docker build -t my-app .
docker tag my-app:latest 123456789.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
docker push 123456789.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
# Create ECS cluster
aws ecs create-cluster --cluster-name my-app-cluster
# Register task definition
aws ecs register-task-definition --cli-input-json file://task-definition.json
Complete Working Example
Let us build and deploy a production-ready Express application to Elastic Beanstalk with custom configuration, rolling deployments, and auto-scaling.
Project Structure
my-app/
.ebextensions/
01-packages.config
02-env.config
03-deployment.config
04-scaling.config
05-logs.config
.platform/
hooks/
postdeploy/
01_cleanup.sh
app.js
package.json
Procfile
.ebignore
.gitignore
package.json
{
"name": "my-beanstalk-app",
"version": "1.0.0",
"description": "Production Express app on Elastic Beanstalk",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"engines": {
"node": "20.x"
},
"dependencies": {
"express": "^4.18.2",
"compression": "^1.7.4",
"helmet": "^7.1.0",
"morgan": "^1.10.0",
"cors": "^2.8.5"
}
}
app.js
var express = require("express");
var compression = require("compression");
var helmet = require("helmet");
var morgan = require("morgan");
var cors = require("cors");
var os = require("os");
var app = express();
var port = process.env.PORT || 8080;
// Middleware
app.use(helmet());
app.use(compression());
app.use(cors());
app.use(morgan("combined"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Health check endpoint
app.get("/health", function(req, res) {
res.status(200).json({
status: "healthy",
environment: process.env.NODE_ENV || "development",
hostname: os.hostname(),
uptime: Math.floor(process.uptime()),
memory: {
used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024) + "MB",
total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024) + "MB"
},
timestamp: new Date().toISOString()
});
});
// API routes
app.get("/", function(req, res) {
res.json({
message: "Welcome to the API",
version: "1.0.0",
docs: "/api/docs"
});
});
app.get("/api/items", function(req, res) {
var page = parseInt(req.query.page) || 1;
var limit = parseInt(req.query.limit) || 20;
// Simulated database query
var items = [];
for (var i = 0; i < limit; i++) {
items.push({
id: (page - 1) * limit + i + 1,
name: "Item " + ((page - 1) * limit + i + 1),
createdAt: new Date().toISOString()
});
}
res.json({
data: items,
pagination: {
page: page,
limit: limit,
total: 100
}
});
});
app.post("/api/items", function(req, res) {
var name = req.body.name;
if (!name) {
return res.status(400).json({ error: "Name is required" });
}
res.status(201).json({
id: Date.now(),
name: name,
createdAt: new Date().toISOString()
});
});
// Error handling
app.use(function(req, res) {
res.status(404).json({ error: "Not found" });
});
app.use(function(err, req, res, next) {
console.error("Unhandled error:", err.stack);
res.status(500).json({ error: "Internal server error" });
});
// Start server
app.listen(port, function() {
console.log("Server running on port " + port);
console.log("Environment: " + (process.env.NODE_ENV || "development"));
console.log("PID: " + process.pid);
});
// Graceful shutdown
process.on("SIGTERM", function() {
console.log("SIGTERM received. Shutting down gracefully...");
process.exit(0);
});
Procfile
web: node app.js
.ebignore
The .ebignore file works like .gitignore but specifically for EB deployments. This controls what gets uploaded:
.git
node_modules
.env
*.log
test/
coverage/
.vscode/
.idea/
.ebextensions/01-packages.config
packages:
yum:
git: []
.ebextensions/02-env.config
option_settings:
aws:elasticbeanstalk:application:environment:
NODE_ENV: production
PORT: 8080
aws:elasticbeanstalk:application:
Application Healthcheck URL: /health
.ebextensions/03-deployment.config
option_settings:
aws:elasticbeanstalk:command:
DeploymentPolicy: Rolling
BatchSize: 50
BatchSizeType: Percentage
Timeout: 600
aws:elasticbeanstalk:healthreporting:system:
SystemType: enhanced
.ebextensions/04-scaling.config
option_settings:
aws:autoscaling:asg:
MinSize: 2
MaxSize: 6
aws:autoscaling:trigger:
MeasureName: CPUUtilization
Statistic: Average
Unit: Percent
UpperThreshold: 70
LowerThreshold: 25
UpperBreachScaleIncrement: 2
LowerBreachScaleIncrement: -1
BreachDuration: 5
Period: 5
aws:autoscaling:launchconfiguration:
InstanceType: t3.small
RootVolumeType: gp3
RootVolumeSize: 20
aws:elbv2:listener:default:
ListenerEnabled: true
Protocol: HTTP
.ebextensions/05-logs.config
option_settings:
aws:elasticbeanstalk:cloudwatch:logs:
StreamLogs: true
DeleteOnTerminate: false
RetentionInDays: 14
Deploy It
# Initialize the EB application
eb init my-beanstalk-app --platform "Node.js 20 running on 64bit Amazon Linux 2023" --region us-west-2
# Create the environment and deploy
eb create prod --scale 2 --instance-type t3.small
# Wait for environment to go green, then verify
eb status
eb open
# Future deployments
eb deploy
Common Issues and Troubleshooting
1. Application Fails to Start - Port Binding Error
ERROR Engine execution has encountered an error.
[ERROR] An error occurred during execution of command [app-deploy] -
[Stage 0: Application] - [Step 0: StageApplication]. Stop running the command.
Error: unable to execute command: /bin/sh -c node app.js
Error: listen EADDRINUSE :::8080
This happens when your application tries to bind to a port already in use, or when a previous deployment left a zombie process. Fix: ensure your app reads process.env.PORT and handles SIGTERM for graceful shutdown. If the problem persists, restart the environment:
eb restart
2. npm Install Fails Due to Node Version Mismatch
npm WARN EBADENGINE Unsupported engine {
package: '[email protected]',
required: { node: '>=20.0.0' },
current: { node: 'v18.18.0' }
}
npm ERR! engine Unsupported engine
Your package.json engines field specifies a Node.js version that does not match the platform. Either update your engines field or select the correct platform:
eb config
# Change the platform to match your required Node.js version
3. Health Check Returns 502 During Deployment
WARN Environment health has transitioned from Ok to Degraded.
50.0 % of the requests are failing with HTTP 5xx.
ERROR Instance i-0abc123def456 has been terminated.
ELB health check failed: 502 Bad Gateway
Your application takes too long to start, and the health check times out before it is ready. Increase the health check timeout and add a startup delay:
option_settings:
aws:elasticbeanstalk:environment:process:default:
HealthCheckPath: /health
HealthCheckInterval: 30
HealthyThresholdCount: 3
UnhealthyThresholdCount: 5
Also verify your app actually responds on the /health path and does not crash during startup.
4. Deployment Stuck in "Updating" State
INFO Environment update is starting.
WARN Environment health has transitioned from Ok to Info.
Application update is in progress.
...
(no further output for 30+ minutes)
This usually happens with immutable deployments when the new instances fail health checks but do not fail hard enough to trigger a rollback. Check the instance logs:
eb logs --all
Look in /var/log/eb-engine.log and /var/log/web.stdout.log for the root cause. If the environment is truly stuck, abort the update:
aws elasticbeanstalk abort-environment-update --environment-name prod
5. .ebextensions Config File Not Picked Up
WARN The configuration file .ebextensions/01-packages.config in application version
contains invalid YAML or JSON. Skipping file.
YAML is extremely sensitive to formatting. Use spaces, not tabs. Use a YAML validator before committing. Also verify the file extension is .config, not .yaml or .yml.
6. Environment Variables Not Available at Build Time
If you reference environment variables during npm install or npm run build, they will not be available because Beanstalk sets them after the application is staged. Move build-time configuration to .ebextensions container commands instead:
container_commands:
01_build:
command: "export API_URL=https://api.example.com && npm run build"
Best Practices
Use immutable deployments for production. The extra cost during deployment is worth the automatic rollback safety net. Reserve all-at-once for dev environments only.
Decouple your RDS database from Elastic Beanstalk. If you create an RDS instance through Beanstalk, it gets deleted when you terminate the environment. Create RDS separately and pass the connection string as an environment variable.
Set up a
.ebignorefile. Without it, EB CLI uses.gitignore, which might exclude files you actually need deployed (like.env.exampletemplates). The.ebignorefile gives you explicit control over what gets uploaded.Enable enhanced health reporting. Basic health checks only verify that the instance is reachable. Enhanced health reporting monitors HTTP response codes, request latency, and instance metrics to give you a real picture of application health.
Use
npm ciinstead ofnpm installin production. It is faster, deterministic, and fails ifpackage-lock.jsonis out of sync withpackage.json. Beanstalk runsnpm installby default, but you can override this with a Procfile or platform hook.Always configure graceful shutdown. Handle
SIGTERMin your application to close database connections and finish in-flight requests before exiting. Beanstalk sends SIGTERM before killing processes during deployments.Set the
enginesfield inpackage.json. This documents which Node.js version your app requires and catches platform mismatches early.Use saved configurations for environment consistency. Save your production configuration and use it to create staging and pre-production environments that are identical:
eb config save prod --cfg production-config
eb create staging --cfg production-config
- Monitor deployment history. Every deployment creates an application version in S3. Review them periodically and clean up old versions to avoid hitting the 1000 version limit:
aws elasticbeanstalk describe-application-versions --application-name my-app
- Tag your environments. Use tags for cost allocation and resource organization. This becomes critical when you have multiple applications and environments:
eb tags --add Project=my-app,Team=backend,Environment=production