yusuf 2890a87a46 feat: add db:push script and api.hanoman.co.id to trusted origins 1 kuukausi sitten
..
DEPLOY-FRONTEND-PM2-NGINX.md 2890a87a46 feat: add db:push script and api.hanoman.co.id to trusted origins 1 kuukausi sitten
README-DEPLOY-HANOMAN-SAME-DOMAIN.md 2890a87a46 feat: add db:push script and api.hanoman.co.id to trusted origins 1 kuukausi sitten

README-DEPLOY-HANOMAN-SAME-DOMAIN.md

Deploy Hanoman — skenario A (IP / VPS sama, domain tetap)

Panduan ini untuk mengganti situs yang sudah ada di hanoman.co.id dengan stack baru (frontend Next terpisah + backend Payload + Postgres di Docker), tanpa pindah server dan tanpa mengganti IP yang sudah dipakai domain tersebut.

Asumsi:

  • Record DNS hanoman.co.id dan www.hanoman.co.id sudah mengarah ke VPS yang sama tempat Anda mengerjakan deploy.
  • Repositori frontend terpisah dari backend (hanoman-website-be).
  • Target URL publik setelah selesai:
Peran URL
Situs pengunjung (Next.js frontend) https://hanoman.co.id dan/atau https://www.hanoman.co.id
API Payload, admin, REST /api/... https://api.hanoman.co.id

Frontend memanggil backend lewat variabel NEXT_PUBLIC_* yang mengarah ke https://api.hanoman.co.id, bukan ke path /api di domain utama — menghindari bentrok dengan route Next di root domain.


1. Gambaran arsitektur di VPS

Internet
   │
   ▼
┌────────────────────────┐
│  Nginx :80 / :443      │  SSL (Let’s Encrypt), reverse proxy
└───────────┬────────────┘
            │
       ┌────┴─────┐
       ▼          ▼
┌─────────────┐  ┌──────────────────────────────────┐
│ PM2         │  │ Docker Compose                    │
│ Next.js FE  │  │  • postgres                       │
│ 127.0.0.1   │  │  • payload → 127.0.0.1:3000       │
│ :3001       │  │  • volume ./media → /app/media    │
└─────────────┘  └──────────────────────────────────┘
  • Nginx menerima hanoman.co.id / www → memproksi ke http://127.0.0.1:3001 (frontend).
  • Nginx menerima api.hanoman.co.id → memproksi ke http://127.0.0.1:3000 (Payload).
  • Port 3000 dan 3001 cukup listen di localhost; yang terbuka ke internet adalah 80 dan 443 (Nginx).

2. Prasyarat di VPS

  • OS: Ubuntu 22.04 atau 24.04 LTS (perintah di bawah memakai apt).
  • Docker + Docker Compose (subperintah docker compose).
  • Git, curl, ca-certificates.
  • Akses pengguna ke sudo.
  • Domain: A record berikut mengarah ke IP VPS yang sama:
    • hanoman.co.id
    • www.hanoman.co.id
    • api.hanoman.co.id (tambahkan jika belum ada).

2.1 Cek cepat (sudah terpasang atau belum)

Jalankan di VPS:

lsb_release -a 2>/dev/null || cat /etc/os-release
command -v sudo && sudo -n true 2>/dev/null && echo "sudo: OK" || echo "sudo: perlu password atau belum ada"
docker --version 2>/dev/null || echo "Docker: belum terpasang"
docker compose version 2>/dev/null || echo "docker compose: belum terpasang"
git --version 2>/dev/null || echo "Git: belum terpasang"
curl --version 2>/dev/null | head -1 || echo "curl: belum terpasang"

2.2 Instalasi paket dasar (curl, git, sudo)

Hanya jika belum ada (misalnya image VPS minimal tanpa curl/git):

apt update
apt install -y sudo curl ca-certificates git

Jika Anda bukan root, pakai:

sudo apt update
sudo apt install -y curl ca-certificates git

2.3 Instalasi Docker Engine + Docker Compose (plugin)

Opsi A — dari repositori Ubuntu (cukup untuk panduan ini)

sudo apt update
sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
sudo usermod -aG docker "$USER"

Keluar dari SSH dan masuk lagi (atau jalankan newgrp docker) agar grup docker aktif tanpa sudo untuk perintah docker.

Cek:

docker --version
docker compose version

Jika paket docker-compose-v2 tidak ditemukan (distro lama), gunakan Opsi B.

Opsi B — skrip resmi Docker (Engine + Compose plugin, versi lebih baru)

curl -fsSL https://get.docker.com -o /tmp/get-docker.sh
sudo sh /tmp/get-docker.sh
sudo usermod -aG docker "$USER"
rm /tmp/get-docker.sh

Lalu logout/login atau newgrp docker, lalu:

docker --version
docker compose version

2.4 Uji Docker tanpa sudo

docker run --rm hello-world

Jika error permission denied, pastikan user sudah di grup docker dan sesi login sudah diperbarui.

2.5 Alat lain yang dipakai di panduan ini

Kebutuhan Kapan muncul di panduan Instal jika belum ada
Nginx Bagian reverse proxy sudo apt install -y nginx
Certbot SSL Let’s Encrypt sudo apt install -y certbot python3-certbot-nginx
UFW Firewall sudo apt install -y ufw
build-essential (opsional) Beberapa npm install native sudo apt install -y build-essential

2.6 Node.js (nvm, PM2)

Node dan PM2 dijelaskan di §4; tidak wajib di §2 kecuali Anda ingin menginstal lebih dulu. Ringkas:

# nvm — ikuti §4.1
# PM2 global — setelah Node ada:
npm install -g pm2

3. Persiapan kode backend (hanoman-website-be)

3.1 Origin yang dipercaya (CORS / CSRF)

File src/payload.config.ts memakai array trustedBrowserOrigins untuk cors dan csrf. Setiap origin browser yang mengakses API atau admin harus ada di daftar ini.

Pastikan array tersebut memuat minimal:

  • https://hanoman.co.id
  • https://www.hanoman.co.id
  • https://api.hanoman.co.id (admin Payload dibuka dari subdomain ini)

Simpan, commit, dan gunakan commit ini saat build image di VPS.

3.2 Clone backend di VPS

sudo mkdir -p /opt
sudo chown $USER:$USER /opt
cd /opt
git clone <URL-GIT-BACKEND> hanoman-website-be
cd hanoman-website-be

3.3 File .env backend (di server)

Buat atau salin .env di root hanoman-website-be. Isi wajar (sesuaikan password dan secret):

# Postgres — dipakai service postgres di Docker Compose
POSTGRES_DB=hanoman
POSTGRES_USER=postgres
POSTGRES_PASSWORD=ganti_password_kuat
POSTGRES_HOST=postgres
POSTGRES_PORT=5432

# Koneksi dari container payload ke container postgres (hostname = nama service Compose)
DATABASE_URL=postgresql://postgres:ganti_password_kuat@postgres:5432/hanoman

PAYLOAD_SECRET=ganti_secret_panjang_acak
NODE_ENV=production

# URL publik API (tanpa slash di akhir)
PAYLOAD_SERVER_URL=https://api.hanoman.co.id

# Origin frontend untuk CORS endpoint kontak (skema + host, tanpa path)
FRONTEND_ORIGIN=https://hanoman.co.id

Catatan:

  • DATABASE_URL untuk container wajib memakai hostname postgres, bukan 127.0.0.1.
  • FRONTEND_ORIGIN harus sama dengan origin yang dipakai pengunjung (jika kanonis Anda https://www.hanoman.co.id, isi itu).
  • Variabel untuk email kontak / SMTP (jika dipakai) tetap sesuai kebutuhan proyek Anda.

Amankan file:

chmod 600 .env

3.4 Jalankan Postgres dan Payload

cd /opt/hanoman-website-be
docker compose --env-file .env -f docker-compose.yml up -d --build

Tunggu container postgres sehat, lalu cek:

docker compose ps

3.5 Buat tabel database (sekali, jika DB baru)

Dengan NODE_ENV=production, Payload tidak mendorong schema otomatis. Setelah Postgres jalan, dari folder backend (dengan pnpm dan dependency terpasang):

cd /opt/hanoman-website-be
corepack enable && corepack prepare pnpm@latest --activate
pnpm install --frozen-lockfile
pnpm run db:push

Perintah db:push menjalankan script yang memuat Payload dengan NODE_ENV=development hanya untuk proses tersebut, sehingga schema Drizzle diterapkan ke database.

3.6 Uji backend di localhost VPS

curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3000/api/

Respons HTTP (bukan connection refused) berarti proses payload mendengarkan.

3.7 Buat user admin

Buka di browser (setelah langkah Nginx + SSL di bawah selesai, atau sementara dengan tunnel):
https://api.hanoman.co.id/admin
dan buat akun admin pertama.
(Jika SSL belum ada, Anda bisa uji sementara dari VPS dengan curl ke localhost — untuk login admin biasanya browser + HTTPS.)


4. Node.js, pnpm, dan PM2 (untuk frontend)

4.1 Node (disarankan pakai nvm)

Samakan major Node dengan engines di package.json frontend (misalnya 22):

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

4.2 PM2 global

npm install -g pm2
pm2 startup

Jalankan perintah sudo yang dikeluarkan pm2 startup agar PM2 hidup lagi setelah reboot.


5. Frontend — build dan PM2

5.1 Clone dan install

cd /opt
git clone <URL-GIT-FRONTEND> hanoman-frontend
cd hanoman-frontend
corepack enable
corepack prepare pnpm@latest --activate
pnpm install --frozen-lockfile

5.2 Environment production frontend

Buat .env.production di root frontend. Contoh (sesuaikan nama variabel dengan yang dipakai kode frontend Anda):

NEXT_PUBLIC_PAYLOAD_API_URL=https://api.hanoman.co.id

Aturan praktis:

  • Tanpa slash di akhir URL basis (https://api.hanoman.co.id).
  • Sesuaikan dengan cara kode menyusun path: jika kode menambahkan '/api/posts', basis URL tanpa /api; jika kode menambahkan '/posts' ke basis yang sudah berisi /api, sesuaikan.

Setiap mengubah variabel NEXT_PUBLIC_*, wajib pnpm run build ulang.

5.3 Build

pnpm run build

5.4 PM2 — ecosystem.config.cjs

Di /opt/hanoman-frontend, buat file ecosystem.config.cjs:

module.exports = {
  apps: [
    {
      name: 'hanoman-fe',
      cwd: '/opt/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',
      },
    },
  ],
}

Jalankan:

cd /opt/hanoman-frontend
pm2 start ecosystem.config.cjs
pm2 save

Uji:

curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3001/

6. DNS untuk api.hanoman.co.id

Di panel DNS (Cloudflare, registrar, dll.):

  • Tambah A record: api.hanoman.co.idIP VPS yang sama dengan hanoman.co.id.
  • TTL bisa 300 detik saat pengujian.

Tunggu propagasi (cek dengan dig api.hanoman.co.id +short).


7. Nginx — reverse proxy

7.1 Pasang Nginx

sudo apt update
sudo apt install -y nginx

7.2 Cadangkan konfigurasi situs lama (jika ada)

sudo cp -a /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak.$(date +%F) 2>/dev/null || true

Jika Anda punya file lain untuk domain lama, cadangkan juga sebelum mengganti isinya.

7.3 Server block — API (api.hanoman.co.id)

Buat /etc/nginx/sites-available/hanoman-api:

server {
    listen 80;
    server_name api.hanoman.co.id;

    client_max_body_size 50M;

    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";
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

7.4 Server block — frontend (hanoman.co.id dan www)

Buat /etc/nginx/sites-available/hanoman-www:

server {
    listen 80;
    server_name hanoman.co.id www.hanoman.co.id;

    client_max_body_size 20M;

    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";
        proxy_read_timeout 120s;
    }
}

7.5 Aktifkan situs dan uji konfigurasi

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

8. SSL (Let’s Encrypt)

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx \
  -d api.hanoman.co.id \
  -d hanoman.co.id \
  -d www.hanoman.co.id

Ikuti prompt. Certbot akan menambahkan listen 443 ssl dan pengaturan sertifikat.

Setelah HTTPS aktif:

  1. Pastikan .env.production frontend memakai https://api.hanoman.co.id (bukan http).
  2. Build ulang frontend dan restart PM2:

    cd /opt/hanoman-frontend
    pnpm run build
    pm2 restart hanoman-fe
    
  3. Pastikan PAYLOAD_SERVER_URL di backend https://api.hanoman.co.id, lalu rebuild dan jalankan ulang container backend jika Anda mengubah .env:

    cd /opt/hanoman-website-be
    docker compose --env-file .env -f docker-compose.yml up -d --build
    

9. Firewall

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status

Tidak perlu membuka port 3000 dan 3001 ke publik jika semua lalu lintas lewat Nginx.


10. Cutover dari situs lama (skenario A)

  1. Selesaikan langkah backend (Docker), db:push, frontend (PM2), Nginx, dan SSL sebelum memutuskan mengganti traffic produksi, jika memungkinkan uji dengan hosts file lokal atau subdomain uji.
  2. Cadangkan file Nginx lama yang melayani hanoman.co.id.
  3. Hentikan proses lama yang memakai port 3001 (atau port lain yang bentrok) — misalnya PM2 app lama: pm2 delete nama-app-lama.
  4. Reload Nginx hanya jika nginx -t sukses.

Downtime sering hanya beberapa detik saat reload. Jika perlu halaman maintenance, bisa sementara return 503 di Nginx (opsional).


11. Memperbarui deploy nanti

Frontend:

cd /opt/hanoman-frontend
git pull
pnpm install --frozen-lockfile
# edit .env.production jika perlu
pnpm run build
pm2 restart hanoman-fe

Backend:

cd /opt/hanoman-website-be
git pull
docker compose --env-file .env -f docker-compose.yml up -d --build

Setelah mengubah trustedBrowserOrigins atau PAYLOAD_SERVER_URL, selalu build ulang image backend.


12. Troubleshooting

Gejala Yang dicek
502 ke frontend pm2 status, curl http://127.0.0.1:3001/
502 ke API docker compose ps, curl http://127.0.0.1:3000/api/
CORS / CSRF / fetch gagal Origin https://hanoman.co.id, https://www.hanoman.co.id, https://api.hanoman.co.id ada di trustedBrowserOrigins; PAYLOAD_SERVER_URL=https://api.hanoman.co.id
Mixed content / redirect aneh di admin Semua URL publik memakai https://, bukan http
relation "users" does not exist Jalankan pnpm run db:push sekali saat DB masih kosong
Media 404 Folder media/ di host ter-mount ke container (./media:/app/media) dan file benar-benar ada di disk
Upload admin gagal (413) Naikkan client_max_body_size di blok api.hanoman.co.id

13. Ringkasan port di host

Layanan Alamat lokal Publik
Payload (Docker) 127.0.0.1:3000 https://api.hanoman.co.id
Frontend (PM2) 127.0.0.1:3001 https://hanoman.co.id / https://www.hanoman.co.id
PostgreSQL 127.0.0.1:5432 (opsional dari host) Jangan expose ke internet tanpa kebutuhan

14. Checklist sebelum dianggap selesai

  • A record api.hanoman.co.id mengarah ke IP VPS yang sama.
  • .env backend: DATABASE_URL memakai host postgres, PAYLOAD_SERVER_URL, FRONTEND_ORIGIN, PAYLOAD_SECRET.
  • trustedBrowserOrigins memuat ketiga origin HTTPS di atas.
  • pnpm run db:push sudah dijalankan untuk DB baru.
  • .env.production frontend memakai https://api.hanoman.co.id dan build sudah dijalankan setelah perubahan NEXT_PUBLIC_*.
  • Nginx + Certbot untuk ketiga hostname.
  • ufw aktif; SSH dan Nginx Full diizinkan.
  • chmod 600 untuk .env backend dan file rahasia frontend.

15. Rollback kasar

  • Simpan salinan konfigurasi Nginx sebelum cutover.
  • Untuk mengembalikan perilaku lama: restore file Nginx, sudo nginx -t && sudo systemctl reload nginx, dan jalankan kembali proses/PM2 versi lama jika masih ada di server.

Backup database Postgres (pg_dump) disarankan sebelum migrasi besar atau perubahan schema produksi.