Deploy with PM2
PM2 is a production-grade process manager for Node.js and Bun applications. It provides automatic restarts, clustering, monitoring, and log management.
Installation
Install PM2 Globally
npm install -g pm2
# or
bun install -g pm2
Verify Installation
pm2 --version
Basic Setup
1. Create PM2 Ecosystem File
Create ecosystem.config.js in your project root:
module.exports = {
apps: [{
name: 'bunty-app',
script: 'bun',
args: 'run src/main.ts',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
env_development: {
NODE_ENV: 'development',
PORT: 3001,
}
}]
};
2. Start Your Application
# Start with ecosystem file
pm2 start ecosystem.config.js
# Start in production mode
pm2 start ecosystem.config.js --env production
# Start with custom name
pm2 start ecosystem.config.js --name "my-bunty-app"
Advanced Configuration
Full Ecosystem Configuration
module.exports = {
apps: [{
name: 'bunty-api',
script: 'bun',
args: 'run src/main.ts',
// Instances
instances: 4, // or 'max' for all CPUs
exec_mode: 'cluster',
// Auto restart
autorestart: true,
watch: false,
max_memory_restart: '1G',
// Restart delays
min_uptime: '10s',
max_restarts: 10,
restart_delay: 4000,
// Logs
error_file: './logs/error.log',
out_file: './logs/out.log',
log_file: './logs/combined.log',
time: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
// Environment variables
env: {
NODE_ENV: 'production',
PORT: 3000,
},
// Advanced
kill_timeout: 5000,
wait_ready: true,
listen_timeout: 10000,
// Cron restart (optional)
cron_restart: '0 0 * * *', // Restart daily at midnight
}]
};
Multiple Applications
module.exports = {
apps: [
{
name: 'bunty-api',
script: 'bun',
args: 'run src/main.ts',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000,
}
},
{
name: 'bunty-worker',
script: 'bun',
args: 'run src/worker.ts',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
}
}
]
};
PM2 Commands
Process Management
# Start
pm2 start ecosystem.config.js
# Stop
pm2 stop bunty-app
pm2 stop all
# Restart
pm2 restart bunty-app
pm2 restart all
# Reload (zero-downtime)
pm2 reload bunty-app
# Delete
pm2 delete bunty-app
pm2 delete all
# List all processes
pm2 list
pm2 ls
Monitoring
# Monitor all processes
pm2 monit
# Show process details
pm2 show bunty-app
# Get process info
pm2 info bunty-app
# Dashboard
pm2 plus
Logs
# View all logs
pm2 logs
# View specific app logs
pm2 logs bunty-app
# View only errors
pm2 logs --err
# Clear logs
pm2 flush
# Enable log rotation
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
Cluster Management
# Scale to 4 instances
pm2 scale bunty-app 4
# Scale up by 2
pm2 scale bunty-app +2
# Scale down by 1
pm2 scale bunty-app -1
Zero-Downtime Deployments
Using PM2 Reload
# Graceful reload (zero-downtime)
pm2 reload bunty-app
Application Code for Graceful Shutdown
// src/main.ts
import { BuntyApplication } from '@bunty/common';
async function bootstrap() {
const app = await BuntyApplication.create({
// ... config
});
await app.listen(3000);
// Signal ready to PM2
if (process.send) {
process.send('ready');
}
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Received SIGINT, closing server...');
await app.close();
process.exit(0);
});
}
bootstrap();
Update ecosystem config:
module.exports = {
apps: [{
name: 'bunty-app',
script: 'bun',
args: 'run src/main.ts',
wait_ready: true,
listen_timeout: 10000,
kill_timeout: 5000,
}]
};
Startup Scripts
Auto-start on Server Boot
# Generate startup script
pm2 startup
# This will output a command like:
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u youruser --hp /home/youruser
# Run the generated command
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u youruser --hp /home/youruser
# Save current process list
pm2 save
# To disable startup
pm2 unstartup systemd
Monitoring with PM2 Plus
Enable PM2 Plus
# Link to PM2 Plus
pm2 link <secret_key> <public_key>
# Or register
pm2 plus
Custom Metrics
import pmx from '@pm2/io';
// Custom metric
const activeUsers = pmx.metric({
name: 'Active Users',
value: () => {
return getUserCount();
}
});
// Custom action
pmx.action('clear cache', (reply) => {
clearCache();
reply({ success: true });
});
Environment Management
Multiple Environments
# Start with specific environment
pm2 start ecosystem.config.js --env production
pm2 start ecosystem.config.js --env development
# Update environment
pm2 restart bunty-app --update-env
Using .env Files
// ecosystem.config.js
require('dotenv').config();
module.exports = {
apps: [{
name: 'bunty-app',
script: 'bun',
args: 'run src/main.ts',
env: {
NODE_ENV: 'production',
DATABASE_URL: process.env.DATABASE_URL,
REDIS_URL: process.env.REDIS_URL,
}
}]
};
Deployment Workflow
Git-based Deployment
// ecosystem.config.js
module.exports = {
apps: [{
name: 'bunty-app',
script: 'bun',
args: 'run src/main.ts',
}],
deploy: {
production: {
user: 'deploy',
host: 'your-server.com',
ref: 'origin/main',
repo: 'git@github.com:user/repo.git',
path: '/var/www/bunty-app',
'post-deploy': 'bun install && pm2 reload ecosystem.config.js --env production'
}
}
};
Deploy with:
# Setup
pm2 deploy ecosystem.config.js production setup
# Deploy
pm2 deploy ecosystem.config.js production
# Revert
pm2 deploy ecosystem.config.js production revert 1
Best Practices
1. Use Cluster Mode
{
instances: 'max', // Use all CPUs
exec_mode: 'cluster'
}
2. Set Memory Limits
{
max_memory_restart: '1G'
}
3. Configure Log Rotation
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 100M
pm2 set pm2-logrotate:retain 10
pm2 set pm2-logrotate:compress true
4. Use Wait Ready
{
wait_ready: true,
listen_timeout: 10000
}
5. Health Checks
{
health_check: {
path: '/health',
interval: 30000, // 30 seconds
timeout: 5000
}
}
Troubleshooting
View Logs
pm2 logs bunty-app --lines 100
Check Process Status
pm2 status
pm2 describe bunty-app
Restart on Error
pm2 restart bunty-app
Clear Everything
pm2 kill
pm2 resurrect
Next Steps
- Set up monitoring
- Configure Docker containers
- Deploy to Kubernetes
- Set up CI/CD pipelines