Deploy Next.js ke VPS IDCloudHost dengan Dokploy

Wahyu Syahputra
Wahyu Syahputra

Dokploy adalah panel deployment open-source yang berjalan di VPS kamu sendiri. Pengalaman pakainya mirip Vercel (connect repo GitHub, push, auto-deploy), tapi semua berjalan di server milikmu dengan biaya bulanan yang jauh lebih murah. Di panduan ini kita deploy aplikasi Next.js ke VM IDCloudHost dari nol sampai punya domain dengan HTTPS.

Contoh aplikasi di panduan ini memakai stack Next.js 15 + Prisma + PostgreSQL + Better Auth (stack yang sama dengan boilerplate yang saya bangun live di YouTube), tapi langkah-langkahnya berlaku untuk hampir semua aplikasi Next.js.

Gambaran alur

VM IDCloudHost (Ubuntu) → hardening → install Dokploy → hubungkan repo GitHub → PostgreSQL di Dokploy → deploy aplikasi → environment variables → setup database → domain + HTTPS → verifikasi.

1. Siapkan VM di IDCloudHost

  1. Login ke console.idcloudhost.com → buat VM baru.
  2. Pilih OS Ubuntu 24.04 LTS.
  3. Spek minimal untuk Dokploy: 2 vCPU / 2 GB RAM / 30 GB disk, tapi saya sarankan 2 vCPU / 4 GB RAM. Build Next.js cukup berat di RAM: dengan 2 GB, RAM nyaris mentok saat build (Dokploy + Traefik + PostgreSQL sudah memakan sekitar 1 GB sebelum build dimulai).
  4. Set username + password (atau SSH key), lalu create.
  5. Pastikan port 22, 80, 443, dan 3000 bisa diakses.

2. Hardening VM

Jangan lewati bagian ini: VM yang baru dibuat dengan login password itu sasaran empuk bot brute-force. Lakukan minimal ini sebelum install Dokploy:

# SSH masuk sebagai user biasa, lalu masuk ke shell root
# karena semua perintah di bawah butuh root:
sudo su -

# a. Update sistem
apt update && apt upgrade -y

# b. Pakai SSH key, lalu matikan login password & root.
# Edit config SSH: vim /etc/ssh/sshd_config
# lalu set dua baris berikut:
#   PasswordAuthentication no
#   PermitRootLogin prohibit-password
systemctl restart ssh

# c. Firewall
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 3000/tcp   # panel Dokploy, tutup lagi setelah setup selesai
ufw enable

# cek rule yang aktif
ufw status            # atau: verbose / numbered
# hapus rule (misal tutup port 3000 setelah setup):
# ufw status numbered → ufw delete <nomor>

# d. Anti brute-force SSH
apt install -y fail2ban

Dua catatan penting yang sering kelewat:

  • Docker mem-bypass UFW. Container yang publish port (misal PostgreSQL dengan -p 5432:5432) tetap terekspos ke publik walaupun UFW tidak membuka port itu, karena trafik ke container lewat chain FORWARD (chain DOCKER/DOCKER-USER milik Docker), bukan chain INPUT tempat rule UFW berada. Solusinya bukan di UFW, tapi: jangan expose port database di Dokploy sama sekali. Biarkan aplikasi connect lewat network internal Docker. ufw status juga tidak menampilkan port yang dipublish Docker; untuk cek port yang benar-benar listening, pakai sudo ss -tlnp.
  • Panel Dokploy di port 3000 adalah akses admin penuh ke server. Setelah setup selesai, beri panel domain sendiri + HTTPS (misal panel.domainkamu.com) lalu tutup port 3000 dari publik, atau minimal pakai password kuat + aktifkan 2FA di Dokploy.

3. Install Dokploy

Masih di shell root yang sama dari langkah 2 (hasil sudo su -), jalankan installer resminya:

curl -sSL https://dokploy.com/install.sh | sh

(Kalau sesi SSH sempat terputus: SSH lagi sebagai user biasa lalu sudo su -, karena login root langsung sudah dimatikan di langkah 2.)

Setelah selesai, buka http://<IP-VM>:3000 di browser → buat akun admin Dokploy pertama.

4. Hubungkan GitHub ke Dokploy

Pastikan kode aplikasimu sudah ada di repo GitHub (private juga bisa). Lalu di Dokploy: Settings → Git → GitHub → Create GitHub App.

  • Toggle Organization?: matikan (OFF) kalau repo ada di akun pribadimu. Toggle ini menentukan di mana GitHub App terdaftar; kalau ON, butuh approval owner organization.
  • Nama app bebas, misal dokploy-namakamu.
  • Setelah app dibuat, statusnya masih “Action Required”, artinya app terdaftar tapi belum di-install. Klik → install → Only select repositories → pilih repo aplikasimu.

Soal keamanan: GitHub App itu semacam “akun robot” ber-izin terbatas: akses kode read-only (write hanya untuk pull request, dipakai fitur preview deployment), scope dibatasi ke repo yang dipilih, dan bisa dicabut kapan saja dari GitHub Settings → Installations. Kredensial app tersimpan di panel Dokploy, satu alasan lagi kenapa port 3000 harus diamankan.

5. Buat project + database PostgreSQL

  1. Create Project → beri nama sesuai aplikasimu.

  2. Di dalam project: Create Service → Database → PostgreSQL. Isi form:

    • Name: label di UI saja, bebas.
    • App Name: (auto). Penting: ini jadi hostname internal database untuk DATABASE_URL.
    • Database Name: nama database aplikasimu.
    • Database User: kosongkan (default postgres).
    • Database Password: pakai yang di-generate → salin dan simpan.
    • Docker image: samakan dengan versi PostgreSQL yang kamu pakai lokal, misal postgres:16-alpine.
  3. Deploy database-nya. Connection string internalnya:

    postgresql://postgres:<password>@<app-name-db>:5432/<nama-database>?schema=public

    Aplikasi dan database berada di satu network Docker internal, jadi jangan expose port 5432 ke publik (ingat catatan Docker vs UFW di atas).

6. Buat application untuk Next.js

  1. Create Service → Application → source: GitHub → pilih repo + branch main.
  2. Build type: Nixpacks (auto-detect pnpm + Next.js, tidak perlu Dockerfile). Kolom Publish Directory kosongkan (itu hanya untuk static site).
  3. Kalau kamu pakai Prisma dengan postinstall: prisma generate di package.json, script itu jalan otomatis saat pnpm install. Prosesnya tidak butuh koneksi database, aman saat build, dan tidak perlu setting apa pun.

⚠️ Gotcha: versi Node

Nixpacks default memilih Node 18, sedangkan Prisma 7 butuh Node 20.19+ / 22.12+ / 24+, sehingga pnpm install gagal dengan error Prisma only supports Node.js versions 20.19+.... Pilih salah satu fix:

  • Permanen (disarankan): tambah di package.json:

    "engines": { "node": "22.x" }
  • Cepat tanpa commit: env var NIXPACKS_NODE_VERSION=22 di tab Environment.

7. Set environment variables

Di tab Environment aplikasi, isi sesuai .env.example project-mu. Untuk stack contoh (Prisma + Better Auth):

DATABASE_URL=postgresql://postgres:<password>@<app-name-db>:5432/<nama-database>?schema=public
BETTER_AUTH_SECRET=<hasil: openssl rand -base64 32>
BETTER_AUTH_URL=https://app.domainkamu.com   # pakai http://<IP> dulu kalau belum ada domain

Lalu klik Deploy dan tunggu build selesai.

8. Pasang domain + HTTPS

  1. Buka tab DomainsAdd Domain. Isi:
    • Host: domain yang mengarah ke IP VM (A record). Belum punya domain? Pakai trik sslip.io: app.<IP-VM>.sslip.io otomatis resolve ke IP itu tanpa setting DNS. Dokploy juga punya tombol dice 🎲 untuk generate domain *.traefik.me gratis.
    • Path: /
    • Container Port: 3000
    • HTTPS: aktifkan, certificate provider Let’s Encrypt (Traefik bawaan Dokploy yang mengurus sertifikatnya).
  2. Save. Traefik langsung merutekan domain ke container, tidak perlu redeploy untuk domainnya.
  3. Kalau kamu pakai Better Auth (atau auth lain yang cek origin): update BETTER_AUTH_URL di tab Environment menjadi persis URL tadi → redeploy. Kalau tidak sinkron, login ditolak karena origin mismatch.

9. Setup database (sekali saja)

Setelah container aplikasi jalan, buka terminal container dari UI Dokploy (menu aplikasi → Terminal), lalu jalankan migrasi + seed sesuai project-mu, misalnya:

pnpm db:setup

Tanpa langkah ini, login pasti gagal karena tabelnya belum ada.

Urutannya penting: deploy dulu supaya container-nya ada, baru jalankan setup database dari terminal container.

10. Verifikasi + auto-deploy

Buka domainnya → pastikan aplikasi jalan dan kamu bisa login. Bonus yang bikin setup ini terasa seperti Vercel: trigger On Push sudah aktif secara default: setiap git push ke main, Dokploy otomatis rebuild dan redeploy.

Dua langkah penutup yang saya sarankan:

  • Amankan panel Dokploy: beri domain sendiri + HTTPS, lalu tutup port 3000 dari publik.
  • Ganti password admin pertama aplikasimu kalau tadi memakai password seed.

Troubleshooting

  • Build error Prisma only supports Node.js versions 20.19+ → Nixpacks memilih Node 18. Fix: engines.node: "22.x" di package.json atau env NIXPACKS_NODE_VERSION=22 (lihat langkah 6).

  • VM 2 GB RAM bisa OOM saat next build. Cek jejaknya: sudo dmesg | grep -i -E "oom|killed process", atau build log yang berhenti mendadak / exit code 137. Fix: upgrade ke 4 GB (8 GB tidak perlu untuk satu aplikasi), dan/atau tambah swap:

    sudo fallocate -l 2G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

    Swap = disk, build jadi 2–3× lebih lambat, tapi gratis. Kombinasi paling anti-gagal: 4 GB RAM + swap 1–2 GB.

  • Login gagal setelah deploy → cek tiga hal berurutan: (1) setup database sudah jalan? (2) BETTER_AUTH_URL sama persis dengan URL yang dibuka? (3) sudah redeploy setelah ganti env?

  • Mau membuktikan Docker mem-bypass UFW? Jalankan docker run -d -p 8080:80 nginx:alpine dan python3 -m http.server 8081 di VM, dua-duanya tidak di-allow UFW. Curl dari luar: port 8080 tembus, 8081 diblokir. Buktinya ada di sudo iptables -t nat -L DOCKER -n.

  • Kalau mau image produksi lebih kecil dan build lebih deterministik: buat Dockerfile sendiri + output: "standalone" di next.config.ts. Untuk kebanyakan kasus, Nixpacks sudah cukup.


Proses deploy seperti ini juga saya praktikkan langsung di channel YouTube Pixel Developer. Kalau kamu lebih suka format nonton sambil praktik, mampir ke arsip episodenya.

Bagikan artikel ini
YouTube X GitHub LinkedIn

© 2026 Pixel Developer · Boilerplate Next.js siap pakai di kurawal.dev