Core Concepts

5 min read
Rapid overview

Docker Core Concepts

Container Fundamentals

What is a Container?

A container is an isolated process with:

  • Its own filesystem (image layers)
  • Its own network namespace
  • Resource limits (cgroups)
  • Process isolation (namespaces)

Docker Architecture

┌─────────────────────────────────────────────┐
│              Docker Client                   │
│         (docker CLI, API calls)             │
└─────────────────┬───────────────────────────┘
                  │ REST API
┌─────────────────▼───────────────────────────┐
│              Docker Daemon                   │
│              (dockerd)                       │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│            containerd                        │
│     (container lifecycle manager)            │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│              runc                            │
│    (OCI runtime - spawns containers)        │
└─────────────────────────────────────────────┘

Dockerfile Best Practices

Basic Dockerfile

FROM node:20-alpine

WORKDIR /app

# Copy dependency files first (better caching)
COPY package*.json ./

RUN npm ci --only=production

# Copy application code
COPY . .

# Non-root user
USER node

EXPOSE 3000

CMD ["node", "server.js"]

Multi-Stage Build

# Build stage
FROM node:20-alpine AS builder

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

# Production stage
FROM node:20-alpine AS production

WORKDIR /app
ENV NODE_ENV=production

# Copy only production dependencies
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Copy built artifacts from builder
COPY --from=builder /app/dist ./dist

USER node
EXPOSE 3000

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

Optimized Go Build

# Build stage
FROM golang:1.22-alpine AS builder

WORKDIR /app

# Download dependencies first
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# Build static binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /server ./cmd/server

# Minimal runtime
FROM scratch

COPY --from=builder /server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

EXPOSE 8080
ENTRYPOINT ["/server"]

Layer Optimization

Order Matters

# Bad: Copies change frequently, invalidates cache
COPY . .
RUN npm install

# Good: Dependencies cached separately
COPY package*.json ./
RUN npm install
COPY . .

Minimize Layers

# Bad: Multiple RUN commands
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
RUN apt-get clean

# Good: Combined commands
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        git && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

Use .dockerignore

# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
Dockerfile*
docker-compose*
*.md
tests/
coverage/
.vscode/

Security Best Practices

Non-Root User

# Create non-root user
RUN addgroup -g 1001 appgroup && \
    adduser -u 1001 -G appgroup -D appuser

# Change ownership
COPY --chown=appuser:appgroup . /app

USER appuser

Read-Only Filesystem

docker run --read-only --tmpfs /tmp myimage

Scan for Vulnerabilities

# Using Docker Scout
docker scout cves myimage:latest

# Using Trivy
trivy image myimage:latest

# Using Snyk
snyk container test myimage:latest

Minimal Base Images

ImageSizeUse Case
scratch0 MBStatic binaries (Go, Rust)
alpine~5 MBGeneral minimal
distroless~20 MBNo shell, security-focused
slim variants~50-100 MBReduced full OS

Docker Compose

Development Setup

# docker-compose.yml
version: '3.8'

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - ./src:/app/src
      - /app/node_modules  # Exclude node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgres://postgres:password@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-network

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge

Production Compose

version: '3.8'

services:
  api:
    image: myregistry/api:${VERSION:-latest}
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
      restart_policy:
        condition: on-failure
        max_attempts: 3
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Networking

Network Types

TypeDescription
bridgeDefault, isolated network on single host
hostContainer shares host's network stack
noneNo networking
overlayMulti-host networking (Swarm)
macvlanContainer gets its own MAC address

Network Commands

# Create network
docker network create --driver bridge my-network

# Connect container to network
docker network connect my-network container-name

# Inspect network
docker network inspect my-network

# List networks
docker network ls

Storage

Volume Types

# Named volume (managed by Docker)
docker volume create mydata
docker run -v mydata:/app/data myimage

# Bind mount (host path)
docker run -v /host/path:/container/path myimage

# tmpfs mount (memory)
docker run --tmpfs /app/cache myimage

Volume Best Practices

# Compose volumes
services:
  db:
    image: postgres
    volumes:
      # Named volume for persistent data
      - db_data:/var/lib/postgresql/data
      # Bind mount for init scripts (read-only)
      - ./init:/docker-entrypoint-initdb.d:ro

volumes:
  db_data:
    driver: local

Common Commands

# Build
docker build -t myapp:v1 .
docker build -t myapp:v1 --no-cache .
docker build -t myapp:v1 --target builder .

# Run
docker run -d --name myapp -p 8080:80 myapp:v1
docker run -it --rm myapp:v1 /bin/sh
docker run --env-file .env myapp:v1

# Inspect
docker ps -a
docker logs -f container-name
docker exec -it container-name /bin/sh
docker inspect container-name

# Clean up
docker system prune -a --volumes
docker image prune -a
docker container prune

# Registry
docker tag myapp:v1 registry.example.com/myapp:v1
docker push registry.example.com/myapp:v1
docker pull registry.example.com/myapp:v1

Interview Questions

1. Explain Docker image layers

  • Each Dockerfile instruction creates a layer
  • Layers are cached and reused
  • Changes in early layers invalidate subsequent layer cache
  • Use multi-stage builds to reduce final image size

2. What's the difference between CMD and ENTRYPOINT?

  • ENTRYPOINT: Defines the executable (harder to override)
  • CMD: Provides default arguments (easily overridden)
  • Combined: ENTRYPOINT ["executable"] + CMD ["arg1", "arg2"]

3. How do you optimize Docker image size?

  1. Use minimal base images (alpine, distroless)
  2. Multi-stage builds
  3. Combine RUN commands
  4. Remove unnecessary files in same layer
  5. Use .dockerignore
  6. Don't install dev dependencies

4. Explain Docker networking modes

  • Bridge: Default isolated network, containers communicate via container names
  • Host: Container uses host's network, no isolation
  • None: No network access
  • Overlay: Cross-host networking for orchestration