AWS Deployment for Node.js | EC2
이 글의 핵심
Deploy Node.js on AWS: EC2 bootstrap with PM2 and Nginx, Elastic Beanstalk with eb CLI, and serverless Lambda with serverless-http. Security groups, env vars, HTTPS, and rollout strategies.
Three AWS Deployment Paths
AWS offers a spectrum of control vs convenience for Node.js applications:
| Option | Control | Maintenance | Best for |
|---|---|---|---|
| EC2 | Full | You manage OS, runtime, web server | Learning, custom configs, cost optimization |
| Elastic Beanstalk | Medium | AWS manages infra, you manage app | Standard web apps without Kubernetes complexity |
| Lambda | Low | AWS manages everything | Spiky/low traffic, microservices, event-driven |
Option 1: EC2 with PM2 and Nginx
EC2 gives you a virtual machine. You install Node.js, PM2 for process management, and Nginx as a reverse proxy.
Launch an EC2 Instance
- AWS Console → EC2 → Launch Instance
- Choose Ubuntu Server 22.04 LTS (or 24.04 LTS)
- Instance type:
t3.micro(free tier) ort3.smallfor production - Security Group: create rules:
- SSH (22) — from your IP only, not 0.0.0.0/0
- HTTP (80) — from anywhere (0.0.0.0/0)
- HTTPS (443) — from anywhere
- Key pair: create and download a
.pemfile - Launch
Initial Server Setup
# Connect via SSH
ssh -i ~/keys/myapp.pem ubuntu@<your-ec2-public-ip>
# Update packages
sudo apt-get update && sudo apt-get upgrade -y
# Install Node.js 20 LTS via NodeSource
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verify
node --version # v20.x.x
npm --version
# Install git
sudo apt-get install -y git
Deploy Your Application
# Clone the repository
git clone https://github.com/yourusername/your-app.git
cd your-app
# Install production dependencies only
npm ci --only=production
# Create .env file
cat > .env << 'EOF'
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:password@db-host:5432/mydb
JWT_SECRET=your-secret-here
EOF
# Restrict .env permissions
chmod 600 .env
PM2 Process Management
# Install PM2 globally
sudo npm install -g pm2
# Start the application (cluster mode uses all CPU cores)
pm2 start app.js --name "my-app" -i max
# Or use an ecosystem config file for better control
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'my-app',
script: 'app.js',
instances: 'max', // one per CPU core
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
max_memory_restart: '500M', // restart if memory exceeds 500MB
error_file: './logs/err.log',
out_file: './logs/out.log',
log_file: './logs/combined.log',
}]
};
EOF
pm2 start ecosystem.config.js
# Set up startup script (runs PM2 on server reboot)
pm2 startup
# Run the command it prints (something like: sudo env PATH=...)
pm2 save
# Useful PM2 commands
pm2 list # show all processes
pm2 logs # tail all logs
pm2 restart my-app # restart without downtime
pm2 reload my-app # zero-downtime reload (cluster mode)
pm2 monit # real-time monitoring dashboard
Nginx Configuration
sudo apt-get install -y nginx
# Create site config
sudo tee /etc/nginx/sites-available/my-app << 'EOF'
server {
listen 80;
server_name your-domain.com www.your-domain.com;
# Serve static files directly — bypass Node.js
location /static/ {
alias /home/ubuntu/your-app/public/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Proxy everything else to Node.js
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade'; # WebSocket support
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
EOF
# Enable the site
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t # validate config
sudo systemctl reload nginx
HTTPS with Let’s Encrypt
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
# Certbot automatically modifies the nginx config for HTTPS
# Auto-renewal is set up automatically
sudo certbot renew --dry-run # test renewal
Deployment Updates
# Update script — run on server
cd /home/ubuntu/your-app
git pull origin main
npm ci --only=production
# Build if needed (TypeScript, etc.)
npm run build
# Zero-downtime reload (PM2 cluster mode)
pm2 reload my-app
Option 2: Elastic Beanstalk
Elastic Beanstalk manages the EC2 instances, load balancer, auto-scaling, and deployment for you. You focus on the application.
Setup
# Install EB CLI
pip install awsebcli --upgrade
# Configure AWS credentials
aws configure
# Enter: Access Key ID, Secret Access Key, region (e.g. us-east-1), output format (json)
# In your project directory
cd your-app
eb init
# Select:
# - Region (us-east-1 or ap-northeast-2 for Korea)
# - Application name
# - Platform: Node.js 20 (running on 64bit Amazon Linux 2023)
# - Whether to use CodeCommit: No (use GitHub)
Create Environment and Deploy
# Create a production environment (this launches EC2 instances + load balancer)
eb create production --instance-type t3.small --min-instances 1 --max-instances 3
# Deploy current code
eb deploy
# Check status
eb status
# Tail logs
eb logs --all
# SSH into the instance for debugging
eb ssh
Environment Variables
# Set environment variables (replaces .env file)
eb setenv \
NODE_ENV=production \
PORT=8080 \
DATABASE_URL=postgresql://user:password@db-host:5432/mydb \
JWT_SECRET=your-secret-here
# View current env vars
eb printenv
Elastic Beanstalk Procfile
# Procfile — tells EB how to start your app
web: node app.js
Or with npm:
web: npm start
Health Check Endpoint
Elastic Beanstalk pings / by default for health checks. Add a dedicated endpoint:
// In your Express app
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});
Then configure EB to use /health:
eb config
# Set HealthCheckPath: /health under aws:elasticbeanstalk:application
Option 3: AWS Lambda (Serverless)
Lambda runs your code in response to events. For HTTP APIs, use API Gateway → Lambda.
Wrap Your Express App
// lambda.js
const serverless = require('serverless-http');
const app = require('./app'); // your Express app
module.exports.handler = serverless(app);
npm install --save serverless-http
serverless.yml Configuration
service: my-app
provider:
name: aws
runtime: nodejs20.x
region: ap-northeast-2
timeout: 30
memorySize: 512
environment:
NODE_ENV: production
DATABASE_URL: ${ssm:/my-app/database-url} # from AWS SSM Parameter Store
functions:
app:
handler: lambda.handler
events:
- http:
path: /
method: ANY
cors: true
- http:
path: /{proxy+}
method: ANY
cors: true
Deploy
npm install -g serverless
serverless deploy --stage production
# View logs
serverless logs -f app --tail
# Invoke manually
serverless invoke -f app --data '{"httpMethod":"GET","path":"/"}'
# Tear down
serverless remove --stage production
Cold Start Mitigation
Lambda has cold starts (first invocation after idle takes extra time). Reduce impact:
# Provisioned concurrency — pre-warm instances (adds cost)
functions:
app:
handler: lambda.handler
provisionedConcurrency: 2
Or use a CloudWatch Events rule to ping Lambda every 5 minutes:
events:
- schedule:
rate: rate(5 minutes)
enabled: true
input:
source: warm-up
Choosing Between the Three
Low traffic / spiky / event-driven
→ Lambda
Standard web app, team unfamiliar with containers
→ Elastic Beanstalk
Custom config, cost-sensitive, or needs containers later
→ EC2 + PM2 + Nginx
→ EC2 + Docker (next step)
→ ECS / EKS (after Docker)
Key Takeaways
- EC2 + PM2 + Nginx: full control, requires most ops work. PM2 handles cluster mode and auto-restart; Nginx handles HTTPS, static files, and proxying; Let’s Encrypt is free SSL
- Elastic Beanstalk: AWS manages infrastructure. Deploy with
eb deploy, set env vars witheb setenv. Add a/healthendpoint for health checks - Lambda: pay per request, no idle cost, auto-scales. Use
serverless-httpto wrap Express. Cold starts are real — use provisioned concurrency for latency-sensitive APIs - Security: security groups should only open the ports you use. Never put secrets in code — use environment variables, AWS SSM Parameter Store, or Secrets Manager
- Zero-downtime deploys: PM2 cluster mode supports
pm2 reload(graceful); Elastic Beanstalk supports rolling deployments out of the box
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Deploy Node.js on AWS: EC2 bootstrap with PM2 and Nginx, Elastic Beanstalk with eb CLI, and serverless Lambda with serve…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- Express.js 완벽 가이드 | Node.js 웹 프레임워크
- Docker Compose로 Node API·PostgreSQL·Redis 한 번에 띄우기
- Node.js 테스트 | Jest, Mocha, Supertest 완벽 가이드
Keywords Covered in This Article (Related Search Terms)
This article covers AWS, EC2, Node.js, Deployment, Elastic Beanstalk, Lambda, Nginx, PM2.