Membawa aplikasi Express ke produksi: mode production dan health check, PM2 cluster mode, Dockerfile multi-stage, reverse proxy nginx, serta strategi deployment ke VPS dan platform cloud.

Selama 23 episode aplikasi kalian hidup di mesin lokal. Episode 24 membawanya ke produksi — dan produksi adalah dunia yang berbeda: proses yang harus dijaga tetap hidup, image yang bisa di-deploy berulang kali, dan reverse proxy yang menangani TLS.
Mengapa episode ini penting? Karena deployment yang baik membuat aplikasi selalu tersedia dan mudah diperbaiki. PM2 menjaga proses tetap hidup dan diskala ke semua CPU; Docker membuat environment identik di mana pun dijalankan; nginx menangani HTTPS dan static yang efisien. Tiga lapis ini adalah pola deployment standar aplikasi Express di 2026.
Sebelum deploy, pastikan aplikasi berjalan di mode produksi — seluruh kebijakan dari config.js episode 16 bergantung padanya (cookie secure, log level, trust proxy):
NODE_ENV=production node src/server.jsLoad balancer, container orchestrator, dan uptime monitor butuh endpoint untuk menanyakan "apakah kamu hidup?":
import mongoose from "mongoose"
import { pool } from "../models/pgPool.js"
app.get("/health", async (req, res) => {
const dbUp = mongoose.connection.readyState === 1
res.status(dbUp ? 200 : 503).json({
status: dbUp ? "ok" : "degraded",
uptime: process.uptime(),
db: dbUp ? "connected" : "disconnected",
})
})Health check yang baik memeriksa ketergantungan nyata (database, Redis) — bukan hanya "server hidup". Platform deployment memakai respons 200/503 ini untuk memutuskan apakah instance sehat.
PM2 (Process Manager 2) adalah pengelola proses Node yang sudah teruji: restart otomatis saat crash, cluster mode lintas CPU, dan log terpusat.
npm install -g pm2pm2 start src/server.js -i max --name express-api
pm2 save
pm2 startup-i max menjalankan satu instance per CPU core — memanfaatkan seluruh CPU untuk aplikasi Node.pm2 save menyimpan daftar proses; pm2 startup membuat PM2 hidup kembali setelah server reboot.Definisikan di file ecosystem.config.cjs agar konsisten:
module.exports = {
apps: [
{
name: "express-api",
script: "src/server.js",
instances: "max",
exec_mode: "cluster",
env: {
NODE_ENV: "production",
},
max_memory_restart: "300M",
},
],
}max_memory_restart me-restart instance yang bocor memori — pertahanan pertama terhadap memory leak.
Tip
pm2 logs, pm2 status, dan pm2 monit adalah dashboard operasional pertama aplikasi kalian. Jika memakai Docker (di bawah), PM2 masuk ke dalam container; tanpa Docker, PM2 berdiri langsung di VPS. Keduanya sah — pilih sesuai lingkungan.
Dockerfile multi-stage memisahkan build (dengan tool lengkap) dari runtime (minimal):
FROM node:22-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
FROM base AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY src ./src
USER node
EXPOSE 3000
CMD ["node", "src/server.js"]Empat keputusan penting:
npm ci --omit=dev — install hanya dependency produksi dari lockfile yang pasti..env — env disuntikkan saat runtime (platform atau -e), bukan di-commit ke image.USER node — aplikasi berjalan sebagai non-root; container yang root adalah risiko keamanan.node_modules
.git
.env*
uploadsHEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:3000/health || exit 1Orchestrator (Docker, K8s, platform) memakai ini untuk mengganti instance yang tidak sehat.
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-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";
}
location /static/ {
alias /var/www/express-api/public/;
expires 7d;
add_header Cache-Control "public";
}
}Header Upgrade/Connection penting untuk WebSocket Socket.IO (episode 14) — tanpa itu, koneksi realtime terputus. Karena nginx kini berada di depan aplikasi, aktifkan trust proxy di aplikasi agar req.ip dan secure cookie berperilaku benar (episode 20 dan 25).
| Platform | Model | Cocok untuk |
|---|---|---|
| VPS (Hetzner/DigitalOcean) | Kontrol penuh, PM2/Docker | Tim yang ingin kontrol total |
| Render / Fly.io | PaaS, deploy dari git | Tim kecil, cepat mulai |
| Railway / Vercel | PaaS khusus Node | API sederhana |
| Kubernetes | Orkestrasi penuh | Skala enterprise (episode 25) |
Build Command: npm ci --omit=dev
Start Command: node src/server.js
Environment: NODE_ENV=production, MONGODB_URI, JWT_SECRET, ...PaaS menangani TLS, restart, dan health check otomatis — kalian hanya perlu menyuntikkan env vars dan memastikan config.js tervalidasi (episode 16).
Warning
Di platform PaaS, jangan menyalin .env ke dalam proyek. Semua secret diisi lewat panel/platform env vars. Aplikasi yang gagal startup karena JWT_SECRET hilang (validasi config episode 16) justru menandakan konfigurasi platform yang benar — lebih baik gagal cepat daripada berjalan dengan secret kosong.
PaaS menyuntikkan PORT secara acak. Aplikasi harus memakai process.env.PORT (kita lakukan sejak episode 3) — bukan port hardcoded 3000.
.env tidak di-copy ke image. Selalu suntikkan saat runtime: docker run -e JWT_SECRET=... atau env platform.
npm install bisa memperbarui lockfile dan menginstall dependency dev. npm ci instal persis dari lockfile — reproduktif dan aman.
Episode 24 membawa aplikasi ke produksi: mode NODE_ENV=production dengan health check, PM2 cluster, Dockerfile multi-stage yang kecil dan aman, reverse proxy nginx dengan TLS dan WebSocket, serta peta platform cloud dari VPS hingga PaaS.
Inti yang harus dibawa pulang:
-i max cluster, pm2 save + pm2 startup untuk persistence.npm ci --omit=dev, tanpa .env, USER node.trust proxy diaktifkan begitu ada proxy/load balancer di depan aplikasi.Di episode 25 selanjutnya kita akan diskala: scaling & high availability — horizontal scale, load balancer, stateless session, observability, dan load testing. Sampai jumpa di episode 25!