Lesson Goal

Understand the role of operating systems, compare virtual machines and containers, and apply Docker fundamentals to package and run applications consistently.

1) Operating System Fundamentals

The operating system is the software layer that mediates between hardware resources and applications. It manages execution, memory, storage, devices, and security boundaries.

2) Virtual Machines vs Containers

CriterionVirtual MachineContainer
IsolationFull guest operating systemShared kernel, isolated processes
WeightHeavier (GBs)Lighter (MBs)
StartupSlower (minutes)Faster (seconds)
Use CaseStrong environment separationApplication packaging and portability

Containers are ideal when the goal is to package an application consistently. Virtual machines remain useful when full operating system separation is required.

3) Simulating Smaller Machines with Docker

Docker can do more than package and run applications. It can also simulate constrained infrastructure by limiting how much RAM and CPU a container may use. This is useful when the team wants to validate application behavior on smaller machines before deploying to the cloud or reproducing production incidents.

FlagPurposeExample
--memoryCaps available RAM--memory="512m"
--cpusLimits CPU quota, approximating fewer vCPUs--cpus="0.5"
mem_limitPersists memory limits in Composemem_limit: 512m
cpusPersists CPU limits in Composecpus: 0.5
docker run --rm --memory="512m" --cpus="0.5" myapp:dev

docker run --rm --memory="1g" --cpus="1.0" -p 8080:80 myapp:staging

services:
  app:
    image: myapp:dev
    mem_limit: 512m
    cpus: 0.5

These limits help answer practical questions: Does the app still start correctly? Does it become too slow under CPU pressure? Does it consume too much memory and crash? This kind of test is especially useful for validating sizing assumptions, reproducing incidents seen only in smaller instances, and catching performance regressions earlier.

4) Docker Hub: Image Types and Trust Levels

Understanding image sources is critical for security and maintainability. Docker Hub provides three tiers of images:

✅ Official Images

Maintained by Docker and verified publishers. Highest trust level with regular security patches and documentation.

docker pull node:20-alpine
docker pull postgres:16
docker pull redis:7
✓ Verified Publisher Images

From verified organizations on Docker Hub. Maintained by the software vendors themselves.

docker pull mysql/mysql-server:8.0
docker pull mongo/mongodb-community-server:latest
⚠️ Community Images

Created by users. Use with caution — always review Dockerfile, check stars/downloads, and verify security before production use.

docker pull someuser/custom-app:1.0

5) Multi-Environment Dockerization Strategy

Different environments require different configurations. Using separate Dockerfiles ensures each environment is optimized for its purpose while maintaining consistency.

Environment Comparison
Aspect Development Staging Production
Base Image node:20-alpine node:20-alpine node:20-alpine (multi-stage)
Dependencies All (dev + prod) Production only Production only
Source Code Live editing enabled Pre-built (dist/) Pre-built (dist/)
Debug Tools Enabled Profiling only Disabled
Security Standard Standard Non-root user
Image Size Larger Medium Minimal

6) Development Dockerfile

Optimized for developer productivity with hot reload, debug tools, and verbose logging.

# Dockerfile.dev
FROM node:20-alpine

WORKDIR /app

# Install all dependencies (dev + production)
COPY package*.json ./
RUN npm ci

# Copy source for live editing
COPY . .

# Development settings
ENV NODE_ENV=development
ENV DEBUG=true

EXPOSE 3000

# Hot reload with ts-node/nodemon
CMD ["npm", "run", "dev"]

Key features: Fast iteration, source code mounted as volume, debug breakpoints enabled, test runners available.

7) Staging Dockerfile

Pre-production validation with realistic constraints. Mirrors production while enabling profiling and integration tests.

# Dockerfile.staging
FROM node:20-alpine

WORKDIR /app

# Production dependencies only
COPY package*.json ./
RUN npm ci --only=production

# Copy pre-built application
COPY dist ./dist

# Staging-specific settings
ENV NODE_ENV=staging
ENV LOG_LEVEL=info
ENV ENABLE_PROFILING=true

EXPOSE 3000

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

Key features: Production-like behavior, profiling enabled for performance analysis, integration test ready.

8) Production Dockerfile (Multi-Stage)

Optimized for security, minimal size, and performance. Uses multi-stage builds to separate build-time and runtime dependencies.

# Dockerfile.prod
# Stage 1: Build
FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Stage 2: Runtime
FROM node:20-alpine

# Non-root user for security
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# Copy only production dependencies and build
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app/dist ./dist

RUN chown -R nodejs:nodejs /app

USER nodejs

# Production settings
ENV NODE_ENV=production
ENV LOG_LEVEL=warn

EXPOSE 3000

CMD ["node", "dist/main.js"]
Multi-Stage Benefits

9) Staged Docker Up Workflow

Docker containerization should be treated as a verification process, not just deployment. Each stage validates specific requirements.

Stage 1: Build
Stage 2: Startup
Stage 3: Business Rules
Stage 4: Full Stack
Stage 1: Load & Build Verification
# Build and verify image creation
docker build -f Dockerfile.dev -t myapp:dev .

# Check image layers and size
docker history myapp:dev
docker images myapp:dev

# Verify TypeScript compilation
docker run --rm myapp:dev npm run build

✅ Verify: Build succeeds, no TypeScript errors, expected image size, all layers cached properly.

Stage 2: Container Startup Tests
# Run with health check
docker run -d --name myapp-test \
  -p 3000:3000 \
  --health-cmd="curl -f http://localhost:3000/health || exit 1" \
  --health-interval=10s \
  --health-retries=3 \
  myapp:dev

# Check container status
docker ps
docker logs myapp-test

# Test health endpoint
curl http://localhost:3000/health

✅ Verify: Container starts, health check passes, no crash loops, environment variables loaded correctly.

10) Stage 3: Business Rules & ORM Verification

Infrastructure requirements are business constraints. Database schemas, migrations, and entity mappings must be validated before deployment.

Data Integrity
  • Unique constraints (emails, usernames)
  • Foreign keys (referential integrity)
  • Not null (required fields)
  • Data types (correct column types)
Transaction Management
  • ACID compliance
  • Rollback on failure
  • Isolation levels
  • Locking strategies
TypeORM / Prisma Infrastructure Checks
# Database connectivity test
docker run -d --name postgres-db \
  -e POSTGRES_DB=myapp \
  -e POSTGRES_USER=admin \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  postgres:16

# Run migration verification (TypeORM)
docker run --rm \
  --link postgres-db:db \
  -e DATABASE_URL=postgresql://admin:secret@db:5432/myapp \
  myapp:dev npm run typeorm migration:run

# Verify entity mappings (Prisma)
docker run --rm \
  -e DATABASE_URL=postgresql://admin:secret@db:5432/myapp \
  myapp:dev npx prisma db pull

# Validate schema sync
docker run --rm \
  -e DATABASE_URL=postgresql://admin:secret@db:5432/myapp \
  myapp:dev npm run schema:validate

# Run integration tests
docker run --rm \
  --link postgres-db:db \
  -e DATABASE_URL=postgresql://admin:secret@db:5432/myapp \
  myapp:dev npm run test:integration

✅ Verify: Database connection, migrations execute successfully, entity mappings correct, relationship constraints enforced, integration tests pass.

11) Stage 4: Full Stack Verification

End-to-end testing in a fully containerized environment that mirrors production architecture.

# docker-compose.test.yml
version: '3.8'
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 10s
      retries: 5
  
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://admin:secret@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
  
  test:
    build:
      context: .
      dockerfile: Dockerfile.dev
    command: npm run test:e2e
    environment:
      DATABASE_URL: postgresql://admin:secret@db:5432/myapp
    depends_on:
      - db

Run command:

docker-compose -f docker-compose.test.yml up --abort-on-container-exit

✅ Verify: All services start correctly, database health checks pass, application connects to database, end-to-end tests execute successfully.

12) Docker as Executable Documentation

Docker is not just a deployment tool — it's living documentation of infrastructure requirements and business constraints.

Requirement Validation

Dockerfile exposes dependencies early, forcing teams to document and validate infrastructure needs before coding begins.

Environment Consistency

dev = staging = production. Eliminates "works on my machine" problems and reduces environment-specific bugs.

Onboarding

New developers run docker-compose up and have a working environment in minutes, not days.

CI/CD Readiness

Same containers used in development run in CI/CD pipelines, ensuring test validity.

💡 Key Insight: Dockerizing early in the development process forces teams to think about infrastructure requirements, dependencies, and business constraints before writing code. This upfront investment pays dividends throughout the project lifecycle.

13) Requirements Traceability Matrix

How Docker stages map to business and technical requirements:

Requirement Type Docker Stage Verification Method
Build Requirements Stage 1 TypeScript compilation, dependency installation
Runtime Requirements Stage 2 Health checks, container stability
Data Integrity Stage 3 Migration execution, schema validation
Business Rules Stage 3 ORM entity mappings, constraint enforcement
Integration Stage 4 End-to-end tests, service communication
Security All Stages Non-root user, minimal dependencies, secrets management

14) Best Practices Summary