Deploy Node.js บน VPS ด้วย PM2 และ Nginx Reverse Proxy
Node.js ไม่มี web server built-in แบบ Apache/Nginx วิธีที่ถูกต้องสำหรับ production คือให้ PM2 จัดการ Node process และให้ Nginx รับ request จาก internet แล้ว forward มาให้ บทความนี้วางขั้นตอนให้ครบ
ติดตั้ง Node.js (LTS)
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
apt install nodejs -y
node -v && npm -v
ติดตั้ง PM2
npm install -g pm2
Deploy Application
# clone หรือ copy project ไปที่ VPS
mkdir -p /var/www/myapp
cd /var/www/myapp
git clone https://github.com/you/myapp.git .
npm install --production
Start App ด้วย PM2
# start
pm2 start app.js --name myapp
# หรือถ้าใช้ npm start script
pm2 start npm --name myapp -- start
# ดู status
pm2 status
pm2 logs myapp
ตั้ง PM2 ให้ Start อัตโนมัติเมื่อ Reboot
pm2 startup systemd
# copy คำสั่งที่ PM2 แสดงมาแล้วรัน
pm2 save
ตั้งค่า Nginx Reverse Proxy
nano /etc/nginx/sites-available/myapp
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
}
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
เพิ่ม SSL ด้วย Certbot
apt install certbot python3-certbot-nginx -y
certbot --nginx -d yourdomain.com
Environment Variables
อย่าเก็บ secret ใน source code ใช้ ecosystem file ของ PM2 แทน:
# ecosystem.config.js
module.exports = {
apps: [{
name: 'myapp',
script: 'app.js',
env: {
NODE_ENV: 'production',
PORT: 3000,
DB_URL: 'mongodb://localhost/mydb'
}
}]
}
# start ด้วย
pm2 start ecosystem.config.js
PM2 Commands ที่ใช้บ่อย
pm2 list # ดู process ทั้งหมด
pm2 restart myapp # restart
pm2 reload myapp # reload แบบ zero-downtime
pm2 stop myapp # stop
pm2 delete myapp # ลบออกจาก list
pm2 monit # dashboard แบบ realtime
pm2 logs myapp --lines 100 # ดู log ย้อนหลัง
pm2 reload แทน restart เมื่อ update code — reload จะ spawn instance ใหม่ก่อนแล้วค่อย kill เก่า ทำให้ไม่มี downtimeสรุป
PM2 + Nginx คือ combination มาตรฐานสำหรับ Node.js production บน VPS PM2 ดูแล process lifecycle และ Nginx ทำหน้าที่ loadbalancer/SSL terminator ทำให้ระบบ robust และ deploy ง่าย
ตรวจสอบว่า Node.js Deploy สำเร็จ
# ดู PM2 status
pm2 status
pm2 list
# ดู log ล่าสุด
pm2 logs myapp --lines 30
# ทดสอบ response
curl -I https://yourdomain.com
# ตรวจว่า process ยังอยู่หลัง reboot
pm2 startup systemd
pm2 save
pm2 start app.js -i max เพื่อรันหลาย instance อัตโนมัติตามจำนวน core ได้ throughput สูงขึ้นโดยไม่ต้องแก้ codeแก้ปัญหาที่พบบ่อย
Error: "EACCES: permission denied, open '/root/...'"
รัน PM2 ด้วย root แล้วพยายาม access ไฟล์ของ user อื่น แนะนำรัน PM2 ด้วย non-root user เสมอ:
# สร้าง user สำหรับ Node app
adduser nodeapp
su - nodeapp
pm2 start /var/www/myapp/app.js --name myapp
Error: "Error: listen EADDRINUSE: address already in use :3000"
Port 3000 มี process อื่นใช้อยู่แล้ว หรือ PM2 start process ซ้ำ:
# ดู process ที่ใช้ port 3000
ss -tlnp | grep :3000
lsof -i :3000
# ถ้า PM2 มี process เก่าค้าง
pm2 delete myapp
pm2 start app.js --name myapp
App crash แล้ว PM2 restart วนซ้ำ (restart loop)
ดู error ก่อนที่จะ crash:
# ดู log ช่วง error
pm2 logs myapp --err --lines 100
# หยุด restart loop ชั่วคราว
pm2 stop myapp
# ทดสอบรัน app ตรงๆ ก่อน
node app.js
Node.js app ช้าหรือ memory leak บน production
# ดู memory ของแต่ละ process
pm2 monit
# ตั้ง max memory restart
pm2 start app.js --name myapp --max-memory-restart 500M
# ตรวจ heap snapshot ด้วย Node debugger
node --inspect app.js
npm install ช้าหรือ fail ด้วย ENOMEM บน VPS RAM น้อย
# เพิ่ม swap ชั่วคราวสำหรับ npm install
fallocate -l 1G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
npm install --production # ติดตั้งเฉพาะ production deps
คำถามที่พบบ่อย
PM2 กับ systemd ต่างกันอย่างไร ควรใช้อะไร?
PM2 ออกแบบมาสำหรับ Node.js โดยเฉพาะ มี cluster mode, log management, monitoring และ restart policy ในตัว systemd เป็น general process manager ที่ OS ใช้ ถ้ามี Node app หลายตัว PM2 จัดการได้ง่ายกว่า
deploy update code ใหม่โดยไม่ให้ downtime ทำยังไง?
# ดึง code ใหม่
git pull origin main
npm install --production
# reload แบบ zero-downtime
pm2 reload myapp # ไม่ใช่ restart
Node.js รองรับ HTTPS เองได้ไหม ไม่ต้องผ่าน Nginx?
รองรับได้ด้วย https.createServer() แต่ไม่แนะนำสำหรับ production เพราะ Nginx มีประสิทธิภาพสูงกว่าในการ serve SSL, static file, compression และ rate limiting ทำให้ Node.js รับแค่ logic งานของ app