Cara Optimasi Dockerfile Node.js dengan Multi-Stage Build

Cara Optimasi Dockerfile Node.js dengan Multi-Stage Build


NEW
docker nodejs devops

Saat mendeploy aplikasi modern berbasis Node.js ke lingkungan produksi, salah satu tantangan utama yang sering dihadapi developer adalah ukuran container image yang sangat besar. Menggunakan konfigurasi standar sering kali menghasilkan image berukuran lebih dari 1 GB. Artikel ini akan membahas cara optimasi Dockerfile Node.js dengan multi-stage build agar ukuran image aplikasi Anda bisa berkurang hingga 80-90% sekaligus meningkatkan keamanan server.

Ukuran image yang kecil berpengaruh langsung pada kecepatan proses CI/CD deployment, efisiensi konsumsi memori/disk di server, serta mengurangi attack surface dari dependensi yang tidak diperlukan pada runtime produksi.


Masalah Umum pada Single-Stage Dockerfile

Banyak pengembang memulai dengan Dockerfile sederhana satu tahap (single-stage build). Berikut adalah contoh skrip Dockerfile konvensional yang kerap dipakai:

FROM node:20

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

EXPOSE 3000
CMD ["npm", "start"]

Meskipun terlihat bersih dan langsung bekerja, pendekatan di atas memiliki beberapa kelemahan kritis:

  1. Ukuran Image Sangat Besar: Menggunakan base image node:20 menyertakan perkakas kompilasi (Python, g++, make) dan OS pendukung penuh yang tidak dibutuhkan saat aplikasi berjalan. Ukuran akhirnya bisa mencapai 1.1 GB.
  2. Sampah Dependensi Development: Folder node_modules menyimpan package devDependencies (seperti TypeScript, ESLint, Vitest/Jest, SWC/Babel) yang seharusnya hanya digunakan saat tahap build.
  3. Keamanan Rendah: Aplikasi dijalankan menggunakan akses root bawaan container, yang berisiko tinggi jika terjadi kerentanan keamanan (security vulnerability).

Memahami Konsep Multi-Stage Build

Fitur Multi-Stage Build pada Docker memungkinkan kita menggunakan beberapa instruksi FROM dalam satu file Dockerfile. Setiap instruksi FROM dapat memulai tahapan (stage) baru dengan base image yang berbeda.

Prinsip utamanya sangat sederhana: kita melakukan kompilasi skrip dan pengunduhan seluruh dependensi pada tahap awal (builder stage), kemudian hanya menyalin hasil build akhir dan dependensi produksi ke tahap akhir (production stage).


Tutorial Langkah demi Langkah

Mari kita susun Dockerfile teroptimasi untuk aplikasi Node.js (misalnya framework Next.js, Express, atau NestJS).

Langkah 1: Tahap Base (Persiapan Environment)

Buat tahapan dasar yang memuat versi Node.js alpine ringkas dan dependensi sistem minimal.

FROM node:20-alpine AS base
WORKDIR /app

Menggunakan node:20-alpine memotong ukuran awal base image dari ~900 MB menjadi hanya ~130 MB.

Langkah 2: Tahap Install Dependensi (deps stage)

Pada tahap ini, kita mengisolasi proses instalasi paket Node.js agar memanfaatkan layer caching Docker secara maksimal.

FROM base AS deps
RUN apk add --no-libc6-compat
COPY package.json package-lock.json ./
RUN npm ci

Langkah 3: Tahap Builder (build stage)

Tahap ini bertugas melakukan kompilasi source code (misalnya mengompilasi TypeScript menjadi JavaScript biasa atau bundling assets).

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

Langkah 4: Tahap Produksi (runner stage)

Tahap akhir ini adalah satu-satunya bagian yang akan di-pack menjadi final image container Anda. Kita hanya mengunduh dependensi produksi (npm ci --omit=dev) dan menyalin file distribusi saja.

FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production

# Tambahkan non-root user untuk keamanan
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 expressjs

# Salin dependensi produksi saja
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force

# Salin hasil kompilasi dari builder stage
COPY --from=builder /app/dist ./dist

USER expressjs

EXPOSE 3000
CMD ["node", "dist/index.js"]

File Dockerfile Lengkap & Teroptimasi

Berikut adalah susunan file Dockerfile utuh yang siap digunakan di proyek Node.js produksi Anda:

# 1. Stage Base
FROM node:20-alpine AS base
WORKDIR /app

# 2. Stage Build Dependencies
FROM base AS deps
COPY package*.json ./
RUN npm ci

# 3. Stage Build Source Code
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# 4. Stage Final Runtime
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force

COPY --from=builder /app/dist ./dist

USER appuser

EXPOSE 3000
CMD ["node", "dist/main.js"]

Perbandingan Hasil Optimasi (Benchmark)

Dengan menerapkan teknik multi-stage build di atas pada aplikasi Node.js/TypeScript menengah, berikut perbandingannya:

Parameter MetrikSingle-Stage StandardMulti-Stage AlpinePersentase Efisiensi
Ukuran Image Final1.15 GB142 MB~87% Lebih Kecil
Jumlah Layer12650% Lebih Ringkas
Waktu Pull/Push Registry45-60 Detik5-8 Detik~85% Lebih Cepat
User Privilegesroot (Unsafe)appuser (Non-root)Aman (Secured)

Tips Tambahan Optimasi Dockerfile

  1. Gunakan .dockerignore: Selalu buat file .dockerignore di root proyek untuk mengabaikan node_modules, .git, dist, .env, dan file log local agar tidak ikut terkirim ke Docker context.
  2. Manfaatkan Docker Layer Cache: Urutkan perintah COPY package*.json sebelum COPY . agar perintah npm ci tidak dieksekusi ulang setiap kali ada perubahan pada file source code.
  3. Pembersihan Cache NPM: Selalu jalankan npm cache clean --force setelah npm ci pada stage runner untuk membersihkan file tarball sisa instalasi package.

Kesimpulan

Mengoptimalkan Dockerfile Node.js menggunakan multi-stage build bukan sekadar pilihan, melainkan best practice wajib di dunia Software Engineering dan DevOps modern. Dengan memisahkan alur kompilasi dan runtime, Anda bisa mendapatkan container image yang ultra-ringan, cepat di-deploy, dan jauh lebih aman.

Segera coba terapkan skrip Dockerfile di atas pada proyek aplikasi Anda dan rasakan peningkatan kecepatan pipeline CI/CD secara signifikan!

© 2026 D.N. Zaki Zamani