MODULE 4/LESSON 1
☸️ Docker & Kubernetes

Docker Container vs VM Model

How Docker containers differ from VMs and why they're the foundation of modern deployment

12 min📊 Diagram
Docker containers share the host kernel — there's no guest OS to boot. This makes them start in milliseconds and use a fraction of VM memory. The real value isn't speed though; it's reproducibility: the same image runs identically on a developer's laptop, CI, staging, and production.

Key Concepts

Images are Immutable

A Docker image is a layered, read-only snapshot. Container = image + writable layer. You can't accidentally change an image in production — you must rebuild it. This is Immutable Infrastructure.

Eliminates 'Works on My Machine'

Image packages the app binary, runtime, libraries, configs, and OS user-space together. Dev, CI, and prod run byte-for-byte identical environments.

Layer Caching Speeds Builds

Each Dockerfile instruction creates a cache layer. Put frequently changing layers (COPY source code) last, rarely changing layers (npm install) first. A 10-minute build becomes 30 seconds.

Multi-stage Builds

Build in a full compiler image, copy only the binary to a minimal runtime image (alpine). Reduces production image from 1.2GB to 50MB, eliminating attack surface.

VM vs Container Architecture

AspectVirtual MachineDocker Container
IsolationFull hardware virtualization (Hypervisor)OS-level namespaces (cgroups, namespaces)
Startup time30-120 seconds (boots full OS)< 1 second (process starts)
Memory overhead500MB–2GB per VM (guest OS)< 10MB per container
Image sizeGB+ (full OS image)MB (layered, shared base layers)
Security isolationStrong (hardware boundary)Process-level (shared kernel — weaker)
Virtual Machine App A Bins/Libs App B Bins/Libs Guest OS Guest OS Hypervisor Host OS Hardware Docker Container App A Bins/Libs App B Bins/Libs Docker Engine Host OS Hardware Guest OS overhead removed

Optimized Dockerfile — NestJS Multi-Stage Build

Dockerfiledockerfile
1# ── Stage 1: Build ──────────────────────────────────
2FROM node:20-alpine AS builder
3WORKDIR /app
4
5# Copy package files first — cached until package.json changes
6COPY package*.json ./
7RUN npm ci --only=production  # Deterministic install
8
9COPY . .
10RUN npm run build             # Compile TypeScript
11
12# ── Stage 2: Runtime ─────────────────────────────────
13FROM node:20-alpine AS runtime
14WORKDIR /app
15
16# Non-root user for security (လုံခြုံရေးအတွက် Root မဟုတ်သော User ဖန်တီးခြင်း)
17RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
18
19# Copy only what's needed — no src/, no dev deps
20COPY --from=builder /app/dist ./dist
21COPY --from=builder /app/node_modules ./node_modules
22
23USER appuser
24EXPOSE 3000
25HEALTHCHECK --interval=30s --timeout=5s CMD wget -qO- http://localhost:3000/health || exit 1
26CMD ["node", "dist/main.js"]
27# Final image size: ~95MB vs 1.2GB without multi-stage
💡
Senior Architect Insight: The most impactful Docker optimization is .dockerignore — exclude node_modules, .git, dist/, logs/. Without it, COPY . . sends gigabytes of data to the Docker daemon on every build, making builds 10x slower even with layer caching.