
Next.js เป็น React Framework ที่ได้รับความนิยมสูงสุดในการสร้างเว็บแอปพลิเคชันสมัยใหม่ แม้จะมีแพลตฟอร์มอย่าง Vercel ให้ใช้งานฟรี แต่หลายทีมและนักพัฒนาเลือกที่จะ deploy บน VPS ของตัวเองเพื่อความยืดหยุ่นและการควบคุมที่สมบูรณ์ บทความนี้จะพาคุณผ่านทุกขั้นตอนตั้งแต่เริ่มต้นจนกว่าแอปจะรันบน HTTPS จริงๆ
ทำไมต้อง Deploy Next.js บน VPS แทน Vercel
Vercel เป็นตัวเลือกที่ดีเยี่ยมสำหรับการเริ่มต้น แต่เมื่อโปรเจกต์เติบโตขึ้น มีปัจจัยหลายอย่างที่ทำให้นักพัฒนาหันมาใช้ VPS ของตัวเอง:
- ควบคุมได้เต็ม 100% — กำหนด environment variables, ทรัพยากรเซิร์ฟเวอร์ และ configuration ได้อย่างอิสระ
- ประหยัดค่าใช้จ่าย — VPS 1 เครื่องสามารถรัน Next.js หลาย project พร้อมกันได้ ต่างจาก Vercel ที่คิดค่าใช้จ่ายต่อ project
- Custom domain และ SSL ตามต้องการ — จัดการ subdomain, wildcard SSL และ Nginx configuration ได้เต็มที่
- ไม่มีข้อจำกัด bandwidth หรือ serverless function timeout — เหมาะกับแอปที่ต้องการ long-running process
- เชื่อมกับ database หรือ service ในเครือข่ายเดียวกัน — ลด latency และเพิ่มความปลอดภัย
ข้อกำหนดเบื้องต้น
ก่อนเริ่ม Deploy ตรวจสอบให้แน่ใจว่าคุณมีสิ่งเหล่านี้พร้อมแล้ว:
- VPS Ubuntu 20.04 LTS หรือ 22.04 LTS พร้อม Root Access
- Node.js เวอร์ชัน 18 LTS ขึ้นไป (แนะนำ 20 LTS)
- PM2 — Process Manager สำหรับ Node.js
- Nginx — Web Server สำหรับทำ Reverse Proxy
- โดเมนที่ตั้งค่า DNS A record ชี้มาที่ IP ของ VPS แล้ว
- Git repository ของโปรเจกต์ Next.js
ติดตั้ง 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 20Clone โปรเจกต์และ 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.comCertbot จะแก้ไข 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
คำสั่งที่ควรรู้เมื่อมีปัญหา:
pm2 status— ดูสถานะทุก processpm2 restart nextjs-app— restart appsudo nginx -t— ตรวจ Nginx config syntaxsudo tail -f /var/log/nginx/error.log— ดู Nginx error logsudo ufw allow 'Nginx Full'— เปิด firewall สำหรับ HTTP/HTTPS
เพิ่มประสิทธิภาพ 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: สร้างไฟล์นี้โดยตรงบน VPS ด้วยnano /var/www/your-app/.env.localแล้วกำหนดสิทธิ์chmod 600 .env.localเพื่อให้อ่านได้เฉพาะ owner - ใช้ PM2 Ecosystem File: กำหนด Environment Variables ใน
ecosystem.config.jsในส่วนenv_productionPM2 จะส่งค่าไปให้ Process อัตโนมัติโดยไม่ต้องพึ่ง shell - ใช้ Secret Manager: สำหรับโปรเจกต์ขนาดใหญ่พิจารณาใช้ HashiCorp Vault หรือ AWS Secrets Manager เพื่อดึง Secret ตอน Runtime
- หมุนเวียน Secret สม่ำเสมอ: ตั้ง Policy เปลี่ยน Database Password และ API Key ทุก 90 วัน เพื่อลดความเสี่ยงจาก Credential Leak
# สร้าง .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:
- ตั้งค่า NODE_ENV=production: ตรวจสอบให้แน่ใจว่า Process Manager ส่ง Environment Variable นี้ไป เพราะ Next.js จะปิด Hot Reload, เปิด Minification และเปิด Build Optimizations อัตโนมัติ
- เพิ่ม Security Headers: ตั้งค่า
X-Frame-Options,X-Content-Type-Options,Referrer-PolicyและContent-Security-Policyผ่านnext.config.jsไม่ใช่แค่ผ่าน Nginx - เปิด Image Optimization: ตั้งค่า
images.domainsใน next.config.js เพื่อให้ Next.js สามารถ Optimize รูปภาพจาก Domain ภายนอกได้ - ตั้งค่า Output Standalone: ใช้
output: 'standalone'ใน next.config.js เพื่อให้ Build Output เล็กลงและ Deploy บน Container ได้ง่ายขึ้น - ตรวจสอบ Bundle Size: รัน
ANALYZE=true npm run buildกับ@next/bundle-analyzerเพื่อดูว่า Package ไหนทำให้ Bundle ใหญ่เกินไป
// 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