Panduan Lengkap Multistage Build Docker untuk Aplikasi Node.js Production-Ready
AW
Axel W

Dipublikasikan 10 Juni 2026

Panduan Lengkap Multistage Build Docker untuk Aplikasi Node.js Production-Ready

Image Docker yang besar bukan hanya boros storage, tapi juga memperlambat CI/CD dan meningkatkan attack surface di production. Masalah ini sering dialami developer Node.js yang menempatkan seluruh dependency development, source code, dan build tools ke dalam satu layer image. Solusinya adalah multistage build, sebuah fitur native Docker yang memungkinkan kita memisahkan environment build dan runtime secara elegan. Artikel ini akan membahas panduan praktis dari nol hingga image production yang minimalis dan aman.

Mengapa Image Docker Bisa Membesar

Ketika kamu menjalankan docker build dengan Dockerfile standar, semua layer tersimpan di image final. Dependency development seperti jest, typescript, dan eslint ikut masuk ke production. Ditambah, file sumber, cache build, dan artifact yang tidak perlu juga turut menggemukkan image. Hasilnya, image yang seharusnya 50 MB bisa membengkak menjadi 400 MB atau lebih. Besarnya image berdampak langsung pada waktu pull di registry, transfer bandwidth, dan startup time container di orchestrator seperti Kubernetes.

Apa Itu Multistage Build

Multistage build memungkinkan kita mendefinisikan beberapa stage di dalam satu Dockerfile. Setiap stage bisa menggunakan base image yang berbeda, dan stage akhir hanya menyalin file yang diperlukan dari stage sebelumnya. Artinya, tools build dan file sumber tetap ada di stage pertama, tetapi tidak ikut masuk ke stage final. Ini secara drastis mengurangi ukuran image dan menghilangkan dependency yang tidak diperlukan di runtime. Fitur ini tersedia sejak Docker 17.05 dan kini menjadi standar industri untuk image production.

Langkah 1: Membuat Dockerfile Dasar

Mulailah dengan menulis Dockerfile sederhana yang belum optimal. Buat sebuah proyek Node.js baru atau gunakan proyek yang sudah ada, lalu tambahkan Dockerfile berikut:

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

Build image tersebut dengan perintah docker build -t node-app:single . dan periksa ukurannya menggunakan docker images. Catat angkanya karena nanti akan kita bandingkan dengan hasil multistage build. Pada proyek nyata dengan banyak dependency, angka ini bisa mencapai ratusan megabyte hanya untuk satu service kecil.

Langkah 2: Memisahkan Build dan Runtime

Sekarang ubah Dockerfile menjadi multistage. Stage pertama bertugas menginstall dependency dan membuild aplikasi. Stage kedua hanya menyalin artifact hasil build beserta dependency production:

FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]

Perhatikan penggunaan node:20-alpine di stage kedua. Alpine Linux jauh lebih ringan dibanding Debian-based image. Perintah COPY --from=builder menyalin hanya folder dist dari stage pertama, sehingga file sumber dan devDependencies tertinggal di stage builder. Ini adalah inti dari optimasi multistage.

Langkah 3: Optimasi Layer dengan .dockerignore

Sebelum rebuild, pastikan file yang tidak diperlukan tidak ikut di-COPY. Buat file .dockerignore di root proyek:

node_modules
npm-debug.log
.git
.env
coverage
dist
tests
*.md
.dockerignore
Dockerfile
.vscode

Dengan .dockerignore, Docker daemon tidak mengirimkan file tersebut ke build context. Ini mempercepat proses build dan mencegah file sensitif seperti .env masuk ke image secara tidak sengaja. Selain itu, build context yang lebih kecil juga mengurangi beban I/O saat Docker mengirimkan file ke daemon.

Langkah 4: Menggunakan Alpine atau Distroless

Jika kamu ingin image yang super minimal, pertimbangkan Google Distroless. Image ini tidak memiliki shell, package manager, atau tools sistem. Hanya runtime Node.js dan library esensial:

FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
CMD ["dist/index.js"]

Distroless menghilangkan surface attack lebih jauh karena tidak ada bash atau sh yang bisa dieksploitasi. Namun, debugging menjadi lebih sulit karena tidak bisa docker exec ke dalam container. Gunakan Distroless untuk service yang sudah stabil dan jarang memerlukan debugging manual. Untuk lingkungan yang masih aktif dikembangkan, Alpine adalah pilihan yang lebih seimbang antara ukuran dan kemudahan debugging.

Langkah 5: Verifikasi dan Benchmark

Build kembali image dengan tag baru: docker build -t node-app:multistage .. Bandingkan ukuran dengan docker images | grep node-app. Pada kasus nyata, image bisa mengecil dari 400 MB menjadi 80 MB atau kurang. Selain itu, jalankan container dan pastikan aplikasi tetap berjalan normal: docker run -p 3000:3000 node-app:multistage. Gunakan docker history untuk melihat per-layer size dan memastikan tidak ada layer bloat. Lakukan load test sederhana menggunakan curl atau ab untuk memastikan performa runtime tidak menurun setelah optimasi.

Opsi Lanjutan: Cache Mount dan BuildKit

Untuk build yang lebih cepat, aktifkan BuildKit dan gunakan cache mount untuk dependency. Tambahkan flag --mount=type=cache di Dockerfile:

RUN --mount=type=cache,target=/app/node_modules \
    npm ci

Fitur ini tersedia di Docker BuildKit dan mempercepat build berulang karena tidak perlu mengunduh ulang dependency setiap kali. Cache mount juga mengurangi traffic jaringan di CI/CD pipeline yang menjalankan build secara berkala.

Kesimpulan

Multistage build adalah best practice wajib untuk aplikasi Docker production. Dengan memisahkan stage build dan runtime, menggunakan Alpine atau Distroless, serta mengoptimasi .dockerignore, kamu bisa mengurangi ukuran image hingga 80% sambil meningkatkan keamanan. Mulailah terapkan teknik ini di proyek Node.js kamu dan rasakan perbedaan kecepatan deployment serta efisiensi resource. Untuk referensi lebih lanjut, kunjungi dokumentasi resmi Docker multistage build.