Typescript #

TypeScript sering dianggap “hanya Node.js dengan typing”. Akibatnya, strategi Docker untuk TypeScript sering disamakan mentah-mentah dengan JavaScript, padahal di production, TypeScript punya fase build eksplisit yang harus diperlakukan dengan benar.

Jika salah strategi, aplikasi TypeScript bisa menghasilkan image:

  • 300–600 MB
  • penuh node_modules
  • lambat di-pull Kubernetes

Artikel ini membahas secara mendetail, rasional, dan production-oriented bagaimana membangun Docker image TypeScript yang kecil, aman, dan maintainable, sejajar dengan praktik terbaik di Java (distroless), Python, dan Go.

Realita Ukuran Docker Image TypeScript #

Tanpa Strategi #

SetupUkuran Image
node:latest + ts-node500–700 MB
node:alpine + npm install300–450 MB

Dengan Strategi yang Benar #

SetupUkuran Image
Multi-stage + prune120–180 MB
Distroless Node.js60–110 MB
Bundling (esbuild)30–60 MB

Masalah Utama TypeScript di Docker #

1. TypeScript Tidak Dibutuhkan di Runtime #

  • tsc
  • ts-node
  • source .ts

Semua ini harus mati di runtime image.

2. node_modules Mengandung Dua Dunia #

  • devDependencies (TypeScript, lint, test)
  • production dependencies

Tanpa disiplin, keduanya ikut masuk.

3. Build Tool Bocor ke Runtime #

  • source map
  • test file
  • CLI tool

Prinsip Utama Image TypeScript Production #

Runtime image hanya menjalankan JavaScript hasil build.

Idealnya runtime hanya berisi:

  • Node.js runtime
  • JavaScript hasil transpile
  • Production dependency (atau bahkan tanpa dependency)

Strategi 1: Multi-stage Build (Fondasi Wajib) #

Dockerfile Dasar (Belum Optimal) #

# =====================
# Build stage
# =====================
FROM node:20-alpine AS builder
WORKDIR /app

COPY package*.json tsconfig.json ./
RUN npm ci

COPY src ./src
RUN npm run build   # tsc -> dist/

# =====================
# Runtime stage
# =====================
FROM node:20-alpine
WORKDIR /app

ENV NODE_ENV=production

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json .

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

Ukuran tipikal: 150–200 MB.


Strategi 2: Production Dependency Only #

Gunakan:

npm ci --omit=dev

atau:

npm prune --omit=dev

Hasil:

  • TypeScript compiler tidak ikut
  • Ukuran turun drastis

Strategi 3: Distroless untuk TypeScript #

Dockerfile Distroless #

FROM node:20-alpine AS builder
WORKDIR /app

COPY package*.json tsconfig.json ./
RUN npm ci
COPY src ./src
RUN npm run build
RUN npm prune --omit=dev

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 .

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

Ukuran realistis: 60–110 MB.


Strategi 4: Bundling (Pendekatan Paling Bersih) #

Bundler seperti:

  • esbuild
  • tsup

mengubah aplikasi TypeScript menjadi satu file JavaScript.

Contoh: esbuild #

esbuild src/index.ts \
  --bundle \
  --platform=node \
  --target=node20 \
  --outfile=dist/app.js

Runtime Image #

FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY dist/app.js .
CMD ["app.js"]

Ukuran akhir: 30–60 MB.


Native Dependency: Jebakan Besar #

Package seperti:

  • bcrypt
  • sharp
  • canvas

Akan:

  • membengkakkan image
  • menyulitkan distroless

Solusi:

  • gunakan pure JS alternative
  • atau build khusus OS target

Source Map & Security #

Pastikan:

  • source map tidak ikut production
  • stack trace tetap meaningful
{
  "sourceMap": false
}

Security & Production Readiness #

Non-root User #

Distroless sudah non-root by default.

Logging #

  • STDOUT
  • structured logging

Healthcheck #

Gunakan HTTP endpoint, bukan shell.


Perbandingan Strategi #

Strateginode_modulesUkuranDebuggingRekomendasi
Multi-stageAdaSedangMudahDev
DistrolessAdaKecilSulitProd
BundlingTidak adaSangat kecilMenengahAPI / Serverless

Kapan Strategi Mana Digunakan? #

KondisiPilihan
API productiondistroless
Microservicebundling
Banyak native addonalpine
Serverlessbundling

Penutup #

TypeScript tidak bermasalah di Docker. Yang bermasalah adalah perlakuan TypeScript seperti JavaScript runtime biasa.

Jika fase build dan runtime dipisahkan dengan benar, TypeScript bisa:

  • sekecil Go service
  • lebih ringan dari Java
  • setara distroless Python

Image kecil bukan tujuan—ia adalah konsekuensi dari build boundary yang bersih.

“Jika TypeScript kamu besar di Docker, kemungkinan besar yang kamu deploy bukan aplikasi—tapi toolchain.”

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact