AWS Elastic Beanstalk provides a managed platform for deploying web applications without managing the underlying infrastructure. It handles capacity provisioning, load balancing, auto-scaling, and application health monitoring. This guide covers deploying containerized applications to Elastic Beanstalk from a senior developer's perspective.
Why Elastic Beanstalk
Elastic Beanstalk offers several advantages:
- Managed Infrastructure: AWS handles servers, load balancers, and scaling
- Docker Support: Deploy containers without Kubernetes complexity
- Easy Rollbacks: One-click rollback to previous versions
- Integrated Monitoring: CloudWatch metrics out of the box
- Cost-Effective: Pay only for underlying AWS resources
Initial Setup
Create Elastic Beanstalk Application
- Go to the AWS Management Console
- Search for "Elastic Beanstalk" in Find Services
- Click "Create Application"
- Enter an application name (e.g., "my-docker-app")
- Select "Docker" as the platform
- Choose "Docker running on 64-bit Amazon Linux 2"
- Click "Create Application"
Wait for environment creation (green checkmark indicates success).
Configure Instance Type
The default t2.micro instance often times out during builds. Upgrade to t2.small:
- In the left sidebar, click "Configuration"
- Find "Capacity" and click "Edit"
- Change "Instance Type" from t2.micro to t2.small
- Click "Apply"
Note: t2.small is outside the free tier but prevents build failures.
Docker Configuration
Single Container Deployment
Create Dockerfile in your project root:
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Multi-Container with Docker Compose
Create docker-compose.yml for Elastic Beanstalk:
version: '3.8'
services:
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
ports:
- "80:80"
depends_on:
- api
- frontend
api:
build:
context: ./api
dockerfile: Dockerfile
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
- REDIS_HOST=redis
depends_on:
- redis
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
environment:
- API_URL=http://api:5000
redis:
image: redis:alpine
worker:
build:
context: ./worker
dockerfile: Dockerfile
environment:
- REDIS_HOST=redis
Create nginx/default.conf:
upstream frontend {
server frontend:3000;
}
upstream api {
server api:5000;
}
server {
listen 80;
location / {
proxy_pass http://frontend;
}
location /sockjs-node {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://api;
}
}
CI/CD with Travis CI
IAM User Setup
Create deployment credentials:
- Search for "IAM" in the AWS Console
- Click "Users" → "Add User"
- Enter username: "eb-deploy-user"
- Select "Programmatic Access"
- Click "Attach Existing Policies Directly"
- Search and select "AWSElasticBeanstalkFullAccess"
- Complete creation and save the Access Key ID and secret
Travis Configuration
Add AWS credentials to Travis:
- Go to your Travis Dashboard
- Click repository → "More Options" → "Settings"
- Add environment variables:
- AWSACCESSKEY: Your IAM access key - AWSSECRETKEY: Your IAM secret key
Create .travis.yml:
language: generic
services:
- docker
before_install:
- docker build -t my-app-test -f Dockerfile.dev .
script:
- docker run -e CI=true my-app-test npm test -- --coverage
deploy:
provider: elasticbeanstalk
region: us-east-1
app: my-docker-app
env: my-docker-app-env
bucket_name: elasticbeanstalk-us-east-1-123456789012
bucket_path: my-docker-app
on:
branch: main
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_KEY
Find your bucket name:
- Go to S3 in the AWS Console
- Look for a bucket starting with "elasticbeanstalk-" matching your region
- Copy the full bucket name
Database Configuration with RDS
Create RDS Instance
- Search for "RDS" in the AWS Console
- Click "Create Database"
- Select "PostgreSQL" (or MySQL)
- Choose the "Free tier" template for testing
- Configure:
- DB instance identifier: my-app-db - Master username: admin - Master password: (secure password) 6. Under "Connectivity": - Choose your VPC - Create a new security group: my-app-db-sg 7. Click "Create Database"
Configure Security Groups
Allow Elastic Beanstalk to access RDS:
- Go to EC2 → Security Groups
- Find your RDS security group
- Edit inbound rules
- Add rule:
- Type: PostgreSQL (port 5432) - Source: Security group of your EB environment 5. Save rules
Set Environment Variables
- Go to Elastic Beanstalk → Configuration
- Find "Software" and click "Edit"
- Under "Environment properties," add:
- DATABASEURL: postgres://admin:password@hostname:5432/myapp - RAILSENV: production - SECRETKEYBASE: (generate with rails secret) 4. Click "Apply"
Redis with ElastiCache
Create Redis Cluster
- Search for "ElastiCache" in the AWS Console
- Click "Create" under Redis
- Configure:
- Name: my-app-redis - Node type: cache.t3.micro - Number of replicas: 0 (for development) 4. Select your VPC and subnet group 5. Create a new security group 6. Click "Create"
Configure Security
- Edit the ElastiCache security group
- Add inbound rule:
- Type: Custom TCP (port 6379) - Source: EB environment security group 3. Add an environment variable in EB: - REDIS_HOST: (ElastiCache endpoint without port)
Health Checks and Monitoring
Configure Health Check
Create .ebextensions/healthcheck.config:
option_settings:
aws:elasticbeanstalk:application:
Application Healthcheck URL: /health
aws:elasticbeanstalk:environment:process:default:
HealthCheckPath: /health
HealthCheckInterval: 30
HealthCheckTimeout: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 5
Implement a health endpoint in your app:
// Express.js example
app.get('/health', (req, res) => {
// Check database connection
db.query('SELECT 1')
.then(() => {
res.status(200).json({ status: 'healthy' });
})
.catch(() => {
res.status(503).json({ status: 'unhealthy' });
});
});
CloudWatch Alarms
Create .ebextensions/cloudwatch.config:
Resources:
CPUAlarmHigh:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: "CPU > 80% for 5 minutes"
MetricName: CPUUtilization
Namespace: AWS/EC2
Statistic: Average
Period: 300
EvaluationPeriods: 1
Threshold: 80
ComparisonOperator: GreaterThanThreshold
AlarmActions:
- !Ref NotificationTopic
NotificationTopic:
Type: AWS::SNS::Topic
Properties:
Subscription:
- Endpoint: [email protected]
Protocol: email
Auto Scaling Configuration
Create .ebextensions/autoscaling.config:
option_settings:
aws:autoscaling:asg:
MinSize: 2
MaxSize: 10
aws:autoscaling:trigger:
MeasureName: CPUUtilization
Statistic: Average
Unit: Percent
LowerThreshold: 30
UpperThreshold: 70
LowerBreachScaleIncrement: -1
UpperBreachScaleIncrement: 1
aws:elasticbeanstalk:environment:
LoadBalancerType: application
HTTPS Configuration
Request SSL Certificate
- Go to AWS Certificate Manager (ACM)
- Click "Request Certificate"
- Enter domain: *.example.com
- Validate via DNS or email
- Wait for the certificate to be issued
Configure HTTPS
Create .ebextensions/https.config:
option_settings:
aws:elb:listener:443:
ListenerProtocol: HTTPS
InstanceProtocol: HTTP
InstancePort: 80
SSLCertificateId: arn:aws:acm:region:account:certificate/id
aws:elb:listener:80:
ListenerEnabled: false
Deployment Best Practices
Blue-Green Deployments
For zero-downtime deployments:
- Create a new environment (clone existing)
- Deploy new version to new environment
- Test thoroughly
- Swap environment URLs in the EB console
- Terminate old environment after verification
Rolling Updates
Configure in .ebextensions/deployment.config:
option_settings:
aws:elasticbeanstalk:command:
DeploymentPolicy: Rolling
BatchSizeType: Percentage
BatchSize: 25
Troubleshooting
View Logs
# Using the EB CLI
eb logs
# Or in the AWS Console:
# Elastic Beanstalk → Logs → Request Logs
Common Issues
Build timeout: Increase instance size to t2.small or larger
Container fails to start: Check Docker logs:
eb ssh
docker logs $(docker ps -q)
Database connection refused: Verify security group rules allow traffic from EB to RDS
502 Bad Gateway: Application crashed or not listening on the correct port
Key Takeaways
- Use Docker: Containerize for consistent deployments
- Separate concerns: Database and cache in managed services
- Security groups matter: Configure carefully for service communication
- Environment variables: Never hardcode credentials
- Monitor actively: Set up CloudWatch alarms from day one
- Plan for scaling: Configure auto-scaling before you need it
Elastic Beanstalk provides an excellent balance between infrastructure abstraction and control, making it ideal for teams that want managed deployments without full Kubernetes complexity.