← Back to Node.js Menu | 🏠 Hub
# Dockerfile (in project root) FROM node:20-alpine # base image — Alpine = small (70MB vs 1GB) WORKDIR /app # working directory inside container # Copy package files first (cache layer optimization) COPY package*.json ./ RUN npm ci --only=production # install production deps only # Copy source code COPY src/ ./src/ COPY .env.example .env # example env (real .env via env vars) # Don't run as root USER node EXPOSE 3000 CMD ["node", "src/server.js"] # .dockerignore (like .gitignore for Docker) node_modules .env .git *.log __tests__ *.test.js
# docker-compose.yml
version: '3.9'
services:
app:
build: .
ports:
- "3000:3000" # host:container
environment:
- NODE_ENV=production
- DB_HOST=postgres # service name as hostname
- DB_PORT=5432
- DB_NAME=myapp
- DB_USER=admin
- DB_PASSWORD=secret
- JWT_SECRET=your_secret_here
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
volumes:
- pg_data:/var/lib/postgresql/data # persist data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d myapp"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
pg_data:
redis_data:
# Commands:
# docker-compose up -d ← start all services (detached)
# docker-compose down ← stop
# docker-compose logs -f app ← stream logs
# docker-compose exec app sh ← enter container shell
# PM2 = keep Node.js alive, auto-restart on crash, clustering
npm install -g pm2
# Start app
pm2 start src/server.js --name "myapp"
# Cluster mode — use all CPU cores
pm2 start src/server.js --name "myapp" -i max
# ecosystem.config.js (recommended)
module.exports = {
apps: [{
name: 'myapp',
script: 'src/server.js',
instances: 'max', // or number like 4
exec_mode: 'cluster',
watch: false,
max_memory_restart: '500M',
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 3000
}
}]
};
# Commands
pm2 start ecosystem.config.js --env production
pm2 status # see all processes
pm2 logs myapp # tail logs
pm2 restart myapp # restart
pm2 reload myapp # zero-downtime reload (cluster)
pm2 stop myapp
pm2 delete myapp
pm2 save # save process list
pm2 startup # auto-start on server reboot
# Nginx config: /etc/nginx/sites-available/myapp
server {
listen 80;
server_name myapp.com www.myapp.com;
# Redirect HTTP → HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name myapp.com www.myapp.com;
# SSL certificates (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
# Proxy to Node.js
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # WebSocket
proxy_set_header Connection 'upgrade'; # WebSocket
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Static files (serve directly, bypass Node)
location /uploads/ {
alias /var/www/myapp/uploads/;
expires 30d;
}
}
# SSL with Certbot (Let's Encrypt — FREE)
# sudo certbot --nginx -d myapp.com -d www.myapp.com
# 1. Server setup (Ubuntu 22.04) sudo apt update && sudo apt upgrade -y sudo apt install nginx certbot python3-certbot-nginx -y # 2. Install Node.js via nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash nvm install 20 nvm use 20 # 3. Install PM2 npm install -g pm2 # 4. Clone & setup app git clone git@github.com:user/myapp.git /var/www/myapp cd /var/www/myapp npm ci --only=production cp .env.example .env nano .env # set production values # 5. Start with PM2 pm2 start ecosystem.config.js --env production pm2 save pm2 startup # follow the command it outputs # 6. Setup Nginx + SSL sudo nano /etc/nginx/sites-available/myapp sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx sudo certbot --nginx -d myapp.com # Security: # - Firewall: ufw allow 22,80,443/tcp # - .env file: chmod 600 .env # - Never expose DB port to internet # - Use environment variables, never hardcode secrets