Memilih arsitektur runtime untuk aplikasi Next.js skala besar memengaruhi latensi p99, stabilitas database, dan total biaya kepemilikan (TCO). Pilihan utama terbagi menjadi dua paradigma: Serverless (AWS Lambda, Vercel) dan Self-hosted Node.js Container (Docker, Kubernetes). Keduanya memiliki trade-off fundamental pada level sistem operasi dan jaringan.

1. Perbandingan Teknis Inti

Cold Start dan Runtime Latency

Serverless mengeksekusi kode dalam environment ephemeral. Ketika traffic melonjak, runtime harus menginisialisasi microVM atau container baru, memuat Node.js runtime, mengevaluasi bundle aplikasi Next.js, dan menjalankan runtime SSR. Proses ini menghasilkan latensi cold start berkisar antara 250ms hingga lebih dari 1 detik pada bundle berukuran besar.

Sebaliknya, Node.js Container berjalan sebagai long-running process. Instance selalu siap menerima traffic (warm state), sehingga latensi p99 tetap deterministik tanpa lonjakan overhead inisialisasi environment baru per-request.

Database Connection Pooling (Prisma / Drizzle)

Pada arsitektur serverless, setiap concurrent execution memicu instansiasi terpisah. Jika terjadi lonjakan 1.000 request simultan, serverless platform membuat 1.000 function instances yang masing-masing membuka koneksi database langsung. Hal ini langsung menyebabkan connection starvation pada PostgreSQL atau MySQL (default max_connections biasanya 100-200).

  • Serverless Mitigasi: Wajib menggunakan connection pooler eksternal seperti PgBouncer, AWS RDS Proxy, atau Prisma Accelerate. Perlu dicatat: PgBouncer dalam mode transaction pooling tidak mendukung prepared statements standar tanpa parameter khusus (misal flag ?pgbouncer=true pada Prisma).
  • Container Advantage: Aplikasi mengelola internal pool secara mandiri. Misalnya, 5 replica pod dengan konfigurasi pool size 10 koneksi per instance akan menghasilkan tepat 50 koneksi persisten ke database, sepenuhnya terisolasi dan dapat diprediksi tanpa layer proxy tambahan.

Streaming dan WebSockets

Fitur React Server Components (RSC) dan Server Actions pada Next.js App Router sangat bergantung pada HTTP chunked transfer streaming. Pada platform serverless, reverse proxy sering kali menerapkan buffering sebelum meneruskan respons ke klien, yang dapat merusak responsivitas UI berbasis Suspense. Serverless juga membatasi execution timeout ketat (15-60 detik) dan tidak dapat mempertahankan long-lived TCP connection seperti WebSocket tanpa layanan eksternal (misal AWS API Gateway WebSocket + Redis pub/sub).

Node.js container mendukung streaming HTTP/1.1 dan HTTP/2 secara native tanpa buffer intervensi serta mampu menangani WebSocket, Server-Sent Events (SSE), dan background jobs jangka panjang dalam satu process boundary.

Kompleksitas Observabilitas

Pelacakan distributed tracing di serverless membutuhkan integrasi vendor proprietary atau OpenTelemetry wrapper khusus yang mengekspor data saat function freeze. Cold start metrics sering terpisah dari application metrics. Pada container, monitoring menggunakan Prometheus metrics endpoint standar (/metrics) dan APM agent (misal Datadog, Grafana Tempo) bekerja secara terus-menerus tanpa risiko data drop akibat proses yang dihentikan paksa (SIGKILL).

2. Analisis Biaya Operasional (TCO)

Model penetapan harga menentukan efisiensi finansial berdasarkan pola traffic:

  • Predictable High Traffic: Jika sistem melayani traffic tinggi yang stabil (misal 500-2.000 RPS konstan), serverless menjadi sangat mahal karena billing dihitung per execution duration (GB-seconds) dan volume invocation. Node.js container pada fixed-capacity cluster (seperti AWS EKS atau VM konvensional) memangkas biaya hingga 60-80% lebih murah per juta request.
  • Spiky / Idle Traffic: Untuk traffic musiman atau aplikasi B2B dengan penggunaan minimal di luar jam kerja, serverless unggul melalui fitur scale-to-zero. Container tetap menelan biaya komputasi dasar (idle capacity) meskipun tidak ada request masuk.

3. Setup Optimal: Next.js Standalone Container

Untuk menjalankan Next.js di container secara efisien tanpa membengkakkan image size oleh node_modules penuh, gunakan fitur standalone output.

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
};

module.exports = nextConfig;

Konfigurasi ini memicu Next.js untuk hanya menelusuri modul yang benar-benar digunakan dan menghasilkan folder .next/standalone dengan file server.js minimal.

Berikut multi-stage Dockerfile berbasis Alpine Linux untuk meminimalkan ukuran image hingga di bawah 150MB:

FROM node:20-alpine AS base

# Stage 1: Install dependencies
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Stage 2: Build source
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN npm run build

# Stage 3: Production runner minimal
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy static assets dan standalone server bundle
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]

4. Decision Matrix: Kapan Memilih atau Migrasi

Faktor PenentuServerless (Vercel / AWS Lambda)Node.js Container (Docker / K8s)
Pola TrafficSpiky, tak terduga, scale-to-zero pentingTinggi, stabil, konstan, predictable
Latensi SLA (p99)Toleran terhadap cold start (>300ms)Kritis, butuh deterministik (<50ms)
Database Direct ConcurrencyMemerlukan PgBouncer / RDS ProxyCukup native connection pool internal
Koneksi Persisten / StreamingTerbatas; butuh websocket external bridgeFull native support (SSE, WebSockets)
Kapabilitas TimFokus product/frontend, no dedicated DevOpsMemiliki tim SRE/DevOps untuk manage cluster

Rekomendasi Tindakan: Mulai arsitektur pada Serverless ketika memvalidasi produk atau fase awal pertumbuhan untuk mengeliminasi beban operasional infrastruktur. Lakukan migrasi ke Node.js Container (Kubernetes/ECS) saat baseline traffic harian stabil, biaya invoice serverless melampaui biaya operasional 1-2 DevOps engineer, atau aplikasi membutuhkan kontrol koneksi socket dan latensi tingkat rendah.