Docker Multi-Stage Build untuk Aplikasi Node.js: Panduan Praktis
AW
Axel W

Dipublikasikan 28 Juni 2026

Docker Multi-Stage Build untuk Aplikasi Node.js: Panduan Praktis

Image Docker yang besar bukan hanya memakan storage registry, tapi juga memperlambat deployment dan meningkatkan attack surface di production. Bagi developer Node.js, masalah ini seringkali terjadi karena single-stage Dockerfile menyertakan devDependencies, source TypeScript, bahkan build tools yang sebenarnya tidak diperlukan saat runtime. Di tutorial ini, kita akan mempelajari Docker multi-stage build untuk memperkecil image Node.js secara signifikan.

Docker menyediakan fitur multi-stage build sejak versi 17.05. Fitur ini memungkinkan kita menggunakan beberapa base image dalam satu Dockerfile, di mana setiap stage bisa menyalin artifact tertentu ke stage berikutnya. Dokumentasi resmi Docker membahas konsep ini secara mendalam di docs.docker.com/build/building/multi-stage.

Masalah dengan Single-Stage Dockerfile

Sebagai baseline, mari lihat Dockerfile single-stage yang umum digunakan. File ini memang berfungsi, tapi menghasilkan image yang jauh lebih besar dari seharusnya.

FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]

Image di atas mencakup compiler TypeScript, devDependencies seperti eslint dan jest, serta file sumber .ts yang sudah tidak dibutuhkan setelah proses build selesai. Hasilnya, image bisa mencapai 1 GB atau lebih untuk proyek Node.js standar.

Konsep Multi-Stage Build

Multi-stage build memisahkan proses build dan runtime ke dalam stage yang berbeda. Stage pertama menggunakan image lengkap dengan build tools, sementara stage final hanya berisi artifact yang sudah dicompile beserta production dependencies.

Keuntungan utama dari approach ini adalah:

  • Ukuran image berkurang drastis, seringkali di bawah 200 MB untuk aplikasi Node.js.

  • Stage final tidak mengandung source code asli dan build tools.

  • Layer caching menjadi lebih efisien karena dependensi diinstal di stage terpisah.

Prasyarat

Pastikan Docker versi 20.10 atau lebih baru sudah terpasang. Kamu juga memerlukan proyek Node.js sederhana sebagai bahan percobaan. Jika belum punya, buatlah Express API minimal dengan TypeScript.

Langkah 1: Setup Aplikasi Node.js Sederhana

Buat folder baru dan inisialisasi proyek dengan Express dan TypeScript. Struktur folder ini akan kita gunakan sebagai contoh di seluruh tutorial.

mkdir node-docker-demo
cd node-docker-demo
npm init -y
npm install express
npm install -D typescript @types/express @types/node
npx tsc --init

Buat file src/index.ts dengan endpoint sederhana yang mereturn status server. Pastikan script build dan start sudah terdefinisi di package.json.

Langkah 2: Dockerfile Single-Stage sebagai Baseline

Buat file Dockerfile.single untuk mengukur ukuran image sebelum optimasi. Build image ini dan catat ukurannya sebagai perbandingan nanti.

docker build -f Dockerfile.single -t node-app:single .

Setelah build selesai, jalankan docker images node-app:single untuk melihat ukuran exact. Di lingkungan dengan image node:20, ukuran ini biasanya berkisar antara 900 MB hingga 1.1 GB.

Langkah 3: Implementasi Multi-Stage Dockerfile

Buat file Dockerfile baru dengan dua stage: builder dan production. Stage builder menangani instalasi dependensi dan kompilasi TypeScript, sementara stage production hanya menyalin hasil build dan node_modules production.

# Stage 1: Builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]

Perhatikan penggunaan node:20-alpine sebagai base image. Alpine Linux jauh lebih ringan dibanding Debian-based image. Di stage production, kita hanya menyalin folder dist dari stage builder dan menginstal ulang dependensi dengan flag --only=production.

Langkah 4: Build dan Bandingkan Ukuran Image

Build image multi-stage dan bandingkan hasilnya dengan single-stage.

docker build -t node-app:multi .

Jalankan perintah berikut untuk melihat perbedaan ukuran secara side-by-side:

docker images node-app --format "table {{.Tag}}	{{.Size}}"

Hasilnya biasanya menunjukkan pengurangan ukuran hingga 70-80 persen. Image multi-stage bisa turun ke kisaran 150-200 MB, sementara single-stage tetap di atas 900 MB. Selain hemat storage, image yang lebih kecil juga mempercepat pull dan push ke container registry.

Langkah 5: Best Practices Production

Optimasi tidak berhenti di multi-stage build. Berikut beberapa praktik tambahan untuk Dockerfile production yang lebih aman dan efisien:

  • Gunakan user non-root dengan perintah USER node di stage production agar container tidak berjalan sebagai root.

  • Hapus cache npm setelah instalasi dengan npm cache clean --force untuk mengurangi layer size.

  • Pertimbangkan distroless image jika kamu ingin minimalis ekstrem, meskipun debugging menjadi sedikit lebih sulit.

  • Scan image secara rutin menggunakan docker scan atau Trivy untuk mendeteksi vulnerability di dependencies.

Jika ingin mendalami optimasi lanjutan, pelajari juga teknik BuildKit dan cache mount untuk mengurangi waktu build di CI/CD pipeline.

Kesimpulan

Docker multi-stage build adalah teknik wajib yang harus dikuasai setiap software engineer. Dengan memisahkan environment build dan runtime, kamu tidak hanya mendapatkan image yang lebih ramping, tapi juga meningkatkan keamanan dan kecepatan deployment. Untuk proyek Node.js, kombinasi Alpine Linux, --only=production, dan --from=builder adalah formula yang paling umum dan terbukti efektif.

Referensi lengkap mengenai multi-stage build tersedia di dokumentasi resmi Docker. Selamat mengoptimasi image container milikmu.