Deploy แอป Node.js บน DigitalOcean ด้วย PM2 + Nginx + SSL ฟรี (Let's Encrypt) ทีละขั้น
สอน deploy แอป Node.js (Express/Next.js/NestJS) บน Droplet Ubuntu ด้วย Node ผ่าน nvm, PM2 ให้รันต่อเนื่องและรีสตาร์ตอัตโนมัติ, Nginx reverse proxy และ SSL ฟรีจาก Let's Encrypt พร้อมสคริปต์อัปเดตโค้ด
ภาพรวมสถาปัตยกรรม
เมื่อ deploy แอป Node.js บน Droplet โครงสร้างมาตรฐานคือ
- แอป Node.js ฟังพอร์ตภายใน เช่น 3000 (ไม่เปิดสู่สาธารณะโดยตรง)
- PM2 เป็น process manager ที่ดูแลให้แอปรันตลอด รีสตาร์ตเมื่อพัง และเปิดเองเมื่อเครื่องรีบูต
- Nginx รับคำขอพอร์ต 80/443 จัดการ SSL แล้วส่งต่อ (reverse proxy) ไปยังแอป
- Certbot ขอและต่ออายุใบรับรอง Let's Encrypt อัตโนมัติ
ถ้าไม่อยากดูแลเซิร์ฟเวอร์เอง คู่มือ App Platform deploy จาก Git ให้อัตโนมัติ แต่แบบ Droplet ควบคุมได้เต็มที่และคุมต้นทุนได้ดีเมื่อมีหลายแอปบนเครื่องเดียว
ก่อนเริ่ม
- Droplet Ubuntu LTS ที่ตั้งค่าพื้นฐานและไฟร์วอลล์แล้ว ดู สอนสร้าง Droplet แรก และ คู่มือความปลอดภัยเซิร์ฟเวอร์
- โดเมนที่ชี้ A record มายัง IP ของ Droplet ดู ตั้งค่า DNS บน DigitalOcean
- ขนาดเครื่อง: แอป Express เล็กๆ รันได้บน 1 GiB; Next.js ที่ต้อง build บนเครื่องควรมี 2 GiB ขึ้นไป หรือเพิ่ม swap
ขั้นที่ 1: ติดตั้ง Node.js ด้วย nvm
nvm ช่วยสลับเวอร์ชัน Node ต่อผู้ใช้โดยไม่ต้องใช้ sudo
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v
npm -v
ตรวจคำสั่งติดตั้งล่าสุดและเลขเวอร์ชันของสคริปต์ nvm ที่หน้า GitHub ของโปรเจกต์ nvm ก่อนรัน ทุกครั้งที่ pipe สคริปต์เข้า bash
ขั้นที่ 2: นำโค้ดขึ้นเครื่อง
sudo apt install -y git
cd ~
git clone https://github.com/USER/REPO.git app
cd app
npm ci --omit=dev
- ใช้
npm ciเพื่อให้ได้เวอร์ชัน dependency ตรงกับ lockfile - ถ้าเป็น Next.js/TypeScript ให้รัน
npm ci && npm run buildก่อน - ไฟล์ตั้งค่าลับเก็บใน
.envบนเซิร์ฟเวอร์ ห้าม commit ขึ้น Git
ทดสอบรันก่อน: node server.js หรือ npm start แล้วลอง curl http://127.0.0.1:3000 จากเครื่องเดียวกัน
ขั้นที่ 3: รันด้วย PM2
npm install -g pm2
pm2 start server.js --name myapp
pm2 status
pm2 logs myapp
ให้เริ่มอัตโนมัติเมื่อเครื่องรีบูต
pm2 startup systemd
PM2 จะพิมพ์คำสั่ง sudo env PATH=... pm2 startup ... ออกมา ให้คัดลอกไปรันตามที่แสดง แล้วบันทึกรายการแอป
pm2 save
ไฟล์ ecosystem (แนะนำเมื่อมีหลายแอป)
module.exports = {
apps: [{
name: 'myapp',
script: 'server.js',
instances: 'max',
exec_mode: 'cluster',
max_memory_restart: '400M',
env: { NODE_ENV: 'production', PORT: 3000 }
}]
}
รันด้วย pm2 start ecosystem.config.js โหมด cluster ใช้ได้ทุก vCPU แต่กินหน่วยความจำเพิ่มตามจำนวน instance บนเครื่อง 1 vCPU ไม่ต้องใช้ cluster
ขั้นที่ 4: ตั้ง Nginx เป็น reverse proxy
sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/myapp
ใส่เนื้อหา
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1: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_set_header X-Forwarded-Proto $scheme;
}
}
เปิดใช้และตรวจไวยากรณ์
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo ufw allow 'Nginx Full'
หัวข้อ Upgrade/Connection จำเป็นถ้าแอปใช้ WebSocket (เช่น Socket.IO) และแอปที่อยู่หลัง proxy ต้องตั้ง trust proxy (Express: app.set('trust proxy', 1)) เพื่ออ่าน IP ผู้ใช้จริง
ขั้นที่ 5: SSL ฟรีด้วย Certbot
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run
Certbot แก้ไฟล์ Nginx ให้อัตโนมัติ เพิ่มการ redirect จาก HTTP ไป HTTPS และตั้งตัวต่ออายุอัตโนมัติ คำสั่ง --dry-run ใช้ยืนยันว่าการต่ออายุทำงาน
ขั้นที่ 6: สคริปต์อัปเดตโค้ด
สร้าง ~/deploy.sh
#!/bin/bash
set -e
cd ~/app
git pull --ff-only
npm ci --omit=dev
# npm run build # ถ้ามีขั้น build
pm2 reload myapp
chmod +x ~/deploy.sh แล้วรัน ./deploy.sh ทุกครั้งที่อัปเดต โหมด pm2 reload ในโหมด cluster ทำ zero-downtime ได้ ส่วนโหมดปกติจะรีสตาร์ตสั้นๆ
ความปลอดภัยและความเสถียร
- ไม่รันแอปด้วย root ใช้ผู้ใช้ปกติตลอด
- เปิดเฉพาะพอร์ต 22/80/443 พอร์ต 3000 ไม่ต้องเปิดสาธารณะ ดู Cloud Firewall และ VPC
- จำกัดขนาดคำขอ ใน Nginx ด้วย
client_max_body_size - เปิด log rotation ของ PM2:
pm2 install pm2-logrotate - ตั้ง alert เมื่อ CPU/RAM/ดิสก์สูง ดู Monitoring และ Alerts
- สำรองข้อมูลและฐานข้อมูล ดู Backups และ Snapshots
แก้ปัญหาที่พบบ่อย
| อาการ | สาเหตุที่เป็นไปได้ | วิธีแก้ |
|---|---|---|
| 502 Bad Gateway | แอปไม่ได้รัน หรือพอร์ตไม่ตรง | pm2 status, pm2 logs, ตรวจ proxy_pass |
| แอปล้มทุกครั้งตอน build | RAM ไม่พอ | เพิ่ม swap หรืออัปเกรด Droplet |
| รีบูตแล้วแอปไม่ขึ้น | ยังไม่ได้ pm2 startup + pm2 save | ทำซ้ำสองคำสั่งนี้ |
| WebSocket ขาดตลอด | ไม่ได้ตั้ง Upgrade header | เพิ่มหัวข้อตามตัวอย่างข้างต้น |
| Certbot ไม่ผ่าน | DNS/พอร์ต 80 | ตรวจ A record และไฟร์วอลล์ |
คำถามที่พบบ่อย
ใช้ PM2 หรือ Docker ดี
ถ้ามีแอปเดียวหรือไม่กี่แอป PM2 ง่ายและเบา ถ้าต้องการความสม่ำเสมอของสภาพแวดล้อมหรือรันบริการหลายชนิดรวมกัน ดู ติดตั้ง Docker บน Droplet
รันหลายแอปบน Droplet เดียวได้ไหม
ได้ ให้แต่ละแอปฟังพอร์ตต่างกันและสร้าง server block ของ Nginx แยกตามโดเมน ระวัง RAM รวมของทุกแอป
ต้องใช้ Load Balancer ไหม
ยังไม่จำเป็นสำหรับเว็บเล็ก เมื่อทราฟฟิกโตจนต้องใช้หลายเครื่อง ค่อยดู คู่มือ Load Balancer