Panduan ini menjelaskan pola deployment di VPS: PostgreSQL + Payload (Next.js backend) tetap di Docker, sementara frontend (aplikasi Next.js terpisah) di-build, dijalankan dengan PM2, dan dihadapkan ke internet lewat Nginx (HTTPS).
Catatan: Repositori frontend diasumsikan terpisah dari repositori backend (
hanoman-website-be). Sesuaikan path dan nama folder dengan proyek Anda.
Internet
│
▼
┌──────────────────┐
│ Nginx :80 / :443│ (SSL, reverse proxy)
└────────┬─────────┘
│
┌────┴────┐
│ │
▼ ▼
┌────────┐ ┌─────────────────────────────┐
│ PM2 │ │ Docker Compose │
│ Next │ │ • postgres:5432 │
│ FE │ │ • payload → host :3000 │
│ :3001 │ │ • volume ./media → /app/media│
└────────┘ └─────────────────────────────┘
http://127.0.0.1:3001 (hanya localhost; Nginx yang memproksi ke domain publik).payload memetakan 3000:3000 — di host, API Payload tersedia di http://127.0.0.1:3000 (REST di /api/...).apt).docker compose (lihat README repositori backend).www.example.com → frontendapi.example.com → backend Payload (disarankan subdomain terpisah agar jelas dan tidak bentrok route /api di Next frontend).Di mesin VPS, di direktori repositori backend:
cd /path/to/hanoman-website-be
docker compose --env-file .env -f docker-compose.yml up -d
Pastikan:
.env berisi DATABASE_URL, PAYLOAD_SECRET, kredensial Postgres, dll.media/ di host ter-mount ke container (lihat docker-compose.yml) agar upload media tidak hilang.Uji cepat dari VPS:
curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3000/api/
Jika backend sehat, Anda akan mendapat respons HTTP (bukan koneksi ditolak).
Samakan major Node dengan yang dipakai proyek (cek engines di package.json frontend). Contoh memakai Node 22 via nvm (disarankan):
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
node -v
npm install -g pm2
# opsional: startup otomatis setelah reboot
pm2 startup
# jalankan perintah yang di-print oleh pm2 startup (sudo env ...)
sudo mkdir -p /var/www
sudo chown $USER:$USER /var/www
cd /var/www
git clone <URL-REPO-FRONTEND> hanoman-frontend
cd hanoman-frontend
Pasang paket (contoh pnpm; sesuaikan jika pakai yarn/npm):
corepack enable
corepack prepare pnpm@latest --activate
pnpm install --frozen-lockfile
Buat file .env.production (atau .env sesuai konvensi Next) di root frontend. Minimal:
# URL publik backend Payload (tanpa slash di akhir)
# Gunakan https://api.example.com jika Nginx sudah memproksi ke :3000
NEXT_PUBLIC_PAYLOAD_API_URL=https://api.example.com
Sesuaikan dengan cara frontend Anda menyusun URL:
NEXT_PUBLIC_PAYLOAD_API_URL + '/api/posts' → isi tanpa /api di akhir.NEXT_PUBLIC_PAYLOAD_API_URL + '/posts' → isi dengan https://api.example.com/api.Build-time: NEXT_PUBLIC_* di-embed saat next build; setelah mengubahnya, harus build ulang.
pnpm run build
ecosystem.config.cjsDi root frontend (mis. /var/www/hanoman-frontend), buat ecosystem.config.cjs:
module.exports = {
apps: [
{
name: 'hanoman-fe',
cwd: '/var/www/hanoman-frontend',
script: 'node_modules/next/dist/bin/next',
args: 'start -p 3001 -H 127.0.0.1',
instances: 1,
autorestart: true,
max_memory_restart: '512M',
env: {
NODE_ENV: 'production',
},
},
],
}
Port 3001 dipakai agar tidak bentrok dengan backend di 3000. Sesuaikan jika Anda memakai port lain.
Jalankan:
cd /var/www/hanoman-frontend
pm2 start ecosystem.config.cjs
pm2 save
Uji lokal:
curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3001/
sudo apt update
sudo apt install -y nginx
a) Backend — api.example.com → 127.0.0.1:3000
Buat /etc/nginx/sites-available/hanoman-api:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
b) Frontend — www.example.com → 127.0.0.1:3001
Buat /etc/nginx/sites-available/hanoman-www:
server {
listen 80;
server_name www.example.com example.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Aktifkan situs:
sudo ln -sf /etc/nginx/sites-available/hanoman-api /etc/nginx/sites-enabled/
sudo ln -sf /etc/nginx/sites-available/hanoman-www /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.com -d www.example.com -d example.com
Certbot akan mengubah blok listen 443 ssl dan jadwal renew otomatis.
Setelah HTTPS aktif, pastikan NEXT_PUBLIC_PAYLOAD_API_URL memakai https://api.example.com lalu build ulang frontend dan restart PM2:
cd /var/www/hanoman-frontend
pnpm run build
pm2 restart hanoman-fe
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Port 3000 dan 3001 tidak perlu dibuka ke publik jika hanya diakses lewat Nginx di localhost.
cors: '*' di payload.config.ts; untuk API umum biasanya cukup.POST /api/contact memakai FRONTEND_ORIGIN — set ke origin frontend production, mis. https://www.example.com, agar preflight CORS konsisten.cd /var/www/hanoman-frontend
git pull
pnpm install --frozen-lockfile
# edit .env.production jika perlu
pnpm run build
pm2 restart hanoman-fe
Backend + DB:
cd /path/to/hanoman-website-be
git pull
docker compose --env-file .env -f docker-compose.yml up -d --build
| Gejala | Hal yang dicek |
|---|---|
| 502 Bad Gateway ke FE | pm2 status, curl http://127.0.0.1:3001/ |
| 502 ke API | docker compose ps, curl http://127.0.0.1:3000/api/ |
| Frontend tidak bisa fetch API | NEXT_PUBLIC_* salah atau belum rebuild; URL harus https setelah Certbot |
| Media backend 404 | Volume ./media di Docker dan isi folder media/ di host |
| Layanan | Port (localhost) | Publik |
|---|---|---|
| Payload (Docker) | 3000 |
Via Nginx api.example.com |
| Frontend (PM2) | 3001 |
Via Nginx www.example.com |
| PostgreSQL (Docker) | 5432 (hanya jika perlu; jangan expose ke internet tanpa kebutuhan) |
— |
Dokumen ini hanya menjelaskan frontend + PM2 + Nginx; manajemen secret, backup DB, dan hardening server mengikuti kebijakan Anda.