Deploy Next.js App บน VPS ด้วย PM2 + Nginx

Next.js เป็น React Framework ที่ได้รับความนิยมสูงสุดในการสร้างเว็บแอปพลิเคชันสมัยใหม่ แม้จะมีแพลตฟอร์มอย่าง Vercel ให้ใช้งานฟรี แต่หลายทีมและนักพัฒนาเลือกที่จะ deploy บน VPS ของตัวเองเพื่อความยืดหยุ่นและการควบคุมที่สมบูรณ์ บทความนี้จะพาคุณผ่านทุกขั้นตอนตั้งแต่เริ่มต้นจนกว่าแอปจะรันบน HTTPS จริงๆ

ทำไมต้อง Deploy Next.js บน VPS แทน Vercel

Vercel เป็นตัวเลือกที่ดีเยี่ยมสำหรับการเริ่มต้น แต่เมื่อโปรเจกต์เติบโตขึ้น มีปัจจัยหลายอย่างที่ทำให้นักพัฒนาหันมาใช้ VPS ของตัวเอง:

ข้อกำหนดเบื้องต้น

ก่อนเริ่ม Deploy ตรวจสอบให้แน่ใจว่าคุณมีสิ่งเหล่านี้พร้อมแล้ว:

ติดตั้ง Node.js 20 LTS บน Ubuntu ด้วย NodeSource

วิธีที่แนะนำคือใช้ NodeSource repository ซึ่งให้ Node.js เวอร์ชันล่าสุดและอัพเดตสม่ำเสมอ:

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs node --version # ควรแสดง v20.x.x npm --version

หรือหากต้องการใช้ NVM (Node Version Manager) เพื่อจัดการหลายเวอร์ชัน:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20 nvm alias default 20

Clone โปรเจกต์และ Build

SSH เข้า VPS แล้วดึงโค้ดจาก Git repository:

cd /var/www git clone https://github.com/youruser/your-nextjs-app.git cd your-nextjs-app # ติดตั้ง dependencies npm install --production=false # สร้าง .env.local สำหรับ production cp .env.example .env.local nano .env.local # แก้ไขค่าตามจริง # Build production npm run build

หลัง build สำเร็จจะได้โฟลเดอร์ .next/ ที่ประกอบด้วย static assets และ server bundle ที่พร้อม serve

ติดตั้งและตั้งค่า PM2

PM2 คือ Process Manager ที่จะคอยรัน Next.js ให้ทำงานอยู่ตลอดเวลา แม้หลัง reboot หรือเกิด error:

npm install -g pm2 # รัน Next.js ผ่าน PM2 cd /var/www/your-nextjs-app pm2 start npm --name "nextjs-app" -- start # ดูสถานะ pm2 status pm2 logs nextjs-app # ตั้งให้เริ่มอัตโนมัติเมื่อ VPS reboot pm2 startup systemd # รันคำสั่งที่ PM2 แสดงออกมา (sudo env PATH=...) pm2 save

ค่า default ของ Next.js จะรันบน port 3000 คุณสามารถกำหนด port อื่นได้ผ่าน environment variable PORT=8080

ตั้งค่า Nginx Reverse Proxy

ติดตั้ง Nginx แล้วสร้าง virtual host ที่ forward traffic จาก port 80/443 ไปยัง Next.js บน port 3000:

sudo apt install -y nginx # สร้าง config ไฟล์ใหม่ sudo nano /etc/nginx/sites-available/nextjs-app

ใส่ config ต่อไปนี้:

server { listen 80; server_name yourdomain.com www.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_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } }
sudo ln -s /etc/nginx/sites-available/nextjs-app /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx

ออก SSL ด้วย Certbot Let's Encrypt

ติดตั้ง Certbot และออก SSL Certificate ฟรีทันที:

sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot จะแก้ไข Nginx config อัตโนมัติ เพิ่ม HTTPS listener, redirect HTTP → HTTPS และตั้ง auto-renewal ผ่าน systemd timer ให้ทุกอย่างพร้อมใน 1 คำสั่ง

Deploy อัตโนมัติด้วย Git Pull + PM2 Reload

สร้าง shell script สำหรับ deploy ใหม่ในไม่กี่วินาที:

#!/bin/bash # /var/www/your-nextjs-app/deploy.sh cd /var/www/your-nextjs-app git pull origin main npm install --production=false npm run build pm2 reload nextjs-app --update-env echo "Deploy complete at $(date)"
chmod +x /var/www/your-nextjs-app/deploy.sh # รัน deploy ด้วยคำสั่งเดียว ./deploy.sh

คุณสามารถเชื่อม script นี้กับ GitHub Actions, GitLab CI หรือ webhook ใดๆ เพื่อทำ CD pipeline อัตโนมัติได้ทันที

เพิ่ม environment variables ใน PM2

หากมี environment variables ที่ต้องเปลี่ยนโดยไม่ต้อง rebuild สามารถตั้งค่าใน PM2 ecosystem file:

// ecosystem.config.js module.exports = { apps: [{ name: 'nextjs-app', script: 'node_modules/.bin/next', args: 'start', env_production: { NODE_ENV: 'production', PORT: 3000 } }] }

เคล็ดลับ: ใช้คำสั่ง pm2 logs nextjs-app เพื่อดู log แบบ real-time หรือ pm2 monit เพื่อเปิด dashboard ที่แสดง CPU, RAM และ error rate ของแต่ละ process — ช่วยแก้ปัญหาได้รวดเร็วมาก

ตรวจสอบและ Troubleshoot

คำสั่งที่ควรรู้เมื่อมีปัญหา:

เพิ่มประสิทธิภาพ Next.js ด้วย Nginx Cache

การตั้งค่า Nginx ให้ cache static assets จะลด load บน Node.js process และทำให้ผู้ใช้โหลดหน้าเร็วขึ้นอย่างเห็นได้ชัด เพราะ Next.js สร้างไฟล์ static ใน /_next/static/ ที่ไม่เปลี่ยนแปลงระหว่าง build cycle เดียวกัน:

server { listen 443 ssl; server_name yourdomain.com; # Cache static assets จาก Next.js location /_next/static/ { alias /var/www/your-nextjs-app/.next/static/; expires 1y; add_header Cache-Control "public, immutable"; } # Cache รูปภาพและ media files location /images/ { root /var/www/your-nextjs-app/public; expires 30d; add_header Cache-Control "public"; } # Proxy ทุก request อื่นไปยัง Next.js 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_cache_bypass $http_upgrade; } }

เพิ่ม Gzip compression เพื่อลดขนาด response:

# เพิ่มในส่วน http{} ของ /etc/nginx/nginx.conf gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_min_length 1024; gzip_comp_level 5;

ตั้งค่า PM2 Cluster Mode เพื่อใช้หลาย CPU Core

VPS ที่มี 2 CPU Core ขึ้นไปสามารถรัน Next.js ในโหมด Cluster ได้ ทำให้ PM2 สร้าง Worker Process ตามจำนวน Core และแจก request ด้วย Round-robin อัตโนมัติ:

// ecosystem.config.js (อัพเดต) module.exports = { apps: [{ name: 'nextjs-app', script: 'node_modules/.bin/next', args: 'start', instances: 'max', // ใช้ CPU ทุก Core exec_mode: 'cluster', // Cluster Mode watch: false, max_memory_restart: '512M', env_production: { NODE_ENV: 'production', PORT: 3000 } }] }
# เริ่ม app ด้วย Cluster Mode pm2 start ecosystem.config.js --env production pm2 save # ดูสถานะ Worker แต่ละตัว pm2 list

หมายเหตุ Cluster Mode: ในโหมด Cluster PM2 ใช้ port เดิม (3000) แต่แบ่ง request ระหว่าง Worker อย่างไรก็ตาม Next.js Server Actions และ WebSocket อาจต้องการ Sticky Session จึงควรทดสอบกับ Nginx Upstream ก่อน deploy จริง

ตาราง VPS Spec ที่เหมาะสมกับแต่ละขนาดโปรเจกต์

เลือก Spec ให้เหมาะกับ Traffic และความซับซ้อนของ Next.js App:

ขนาดโปรเจกต์ RAM แนะนำ CPU PM2 Mode หมายเหตุ
Portfolio / Blog เล็ก 1 GB 1 Core fork เพียงพอสำหรับ <1,000 req/วัน
E-Commerce / SaaS กลาง 2–4 GB 2 Core cluster รองรับ 10,000–50,000 req/วัน
App ขนาดใหญ่ / API-heavy 8 GB+ 4+ Core cluster max พิจารณา Load Balancer เพิ่ม

ตั้งค่า Environment Variables ให้ปลอดภัยบน Production

การจัดการ Environment Variables บน Production VPS เป็นเรื่องสำคัญมาก ไม่ควรเก็บค่าสำคัญอย่าง API Key, Database Password หรือ JWT Secret ใน Git Repository เด็ดขาด วิธีที่แนะนำมีดังนี้:

# สร้าง .env.local บน Server โดยตรง (ห้ามเก็บใน Git) nano /var/www/your-nextjs-app/.env.local # ตัวอย่างเนื้อหาใน .env.local DATABASE_URL=mysql://user:password@localhost:3306/mydb NEXTAUTH_SECRET=your-random-secret-here NEXTAUTH_URL=https://yourdomain.com API_KEY=your-api-key # จำกัดสิทธิ์ให้อ่านได้เฉพาะ owner chmod 600 /var/www/your-nextjs-app/.env.local # ตรวจสอบว่าไม่มี .env.local ใน .gitignore ขาด grep ".env.local" .gitignore || echo "คำเตือน: .env.local ไม่ได้ถูก ignore"

ปรับแต่ง Next.js สำหรับ Production — Checklist ที่นักพัฒนามักลืม

ก่อน Launch บน Production VPS จริง มีการตั้งค่าหลายอย่างที่นักพัฒนามักข้ามไปเพราะใช้งานได้บน Development แล้ว แต่บน Production อาจส่งผลกระทบต่อความเร็ว ความปลอดภัย และ SEO การตั้งค่าที่ถูกต้องตั้งแต่แรกช่วยประหยัดเวลาในการแก้ปัญหาในอนาคตได้มาก เพราะปัญหาด้าน Performance และ Security ที่ถูกพบบน Production มักใช้เวลาแก้ไขนานกว่าปัญหาที่พบบน Development environment:

// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { output: 'standalone', images: { domains: ['your-cdn-domain.com'], formats: ['image/avif', 'image/webp'], }, headers: async () => [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'SAMEORIGIN' }, { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, ], }, ], // ปิด Powered-By header เพื่อความปลอดภัย poweredByHeader: false, } module.exports = nextConfig

หลังแก้ไข next.config.js ต้อง build ใหม่เสมอ จากนั้น pm2 reload เพื่อให้ config ใหม่มีผล การตั้งค่าเหล่านี้เป็นขั้นพื้นฐานที่ทุกโปรเจกต์ควรมีก่อน Go Live เพราะการแก้ Security หรือ Performance ทีหลังบน Production ที่รับ Traffic จริงอยู่แล้วมีความเสี่ยงมากกว่า นอกจากนี้การใช้ output standalone ยังช่วยลดขนาด Docker Image ลงได้อย่างมีนัยสำคัญ เมื่อโปรเจกต์ขยายไปสู่การ Deploy ด้วย Container ในอนาคต ทำให้ใช้พื้นที่ Disk น้อยลงและ Pull Image ได้เร็วขึ้น

ตรวจสอบ Memory Leak และ Restart Policy

แอปพลิเคชัน Node.js อาจเกิด Memory Leak เมื่อทำงานนานขึ้น PM2 มีฟีเจอร์ max_memory_restart ที่ restart process อัตโนมัติเมื่อใช้ RAM เกินกำหนด ช่วยให้แอปทำงานได้เสถียรโดยอัตโนมัติ:

# ตรวจสอบ Memory และ CPU realtime pm2 monit # ดู Log ย้อนหลัง 200 บรรทัด pm2 logs nextjs-app --lines 200 # ดู Error Log เฉพาะ pm2 logs nextjs-app --err # ล้าง Log เก่า pm2 flush nextjs-app # ตั้ง restart เมื่อ RAM เกิน 400MB (ใน ecosystem.config.js) # max_memory_restart: '400M'

ตรวจสอบสุขภาพโดยรวมของ VPS ด้วยคำสั่งเหล่านี้เป็นประจำ การ Monitor เชิงรุกช่วยให้ทีมพัฒนาทราบล่วงหน้าก่อนที่ผู้ใช้จะพบปัญหา และยังช่วยวางแผนการ Scale VPS ให้ทันก่อนที่ Resource จะหมด:

# ดู RAM ที่ใช้อยู่ free -h # ดู Disk df -h /var/www # ดู CPU Load top -bn1 | head -5

เช่า VPS เพื่อรัน Next.js

VPS AsiaGB พร้อม Root Access, SSD, และ Uptime 99% เริ่มต้นเพียง 500 บาท/เดือน รองรับ Node.js, PM2 และ Nginx ได้ทันที

ดูแพ็กเกจ VPS