Docker

Docker = Linux containers + smart packaging

Think of Docker as "shipping containers for software": - Same app runs identically on your laptop, AWS, Google Cloud, or your friend’s PC. - No more "It works on my machine!"

Your App (Python, Node.js, Java)
       ↓
Docker packages it with EXACT OS libraries
       ↓
Runs anywhere Docker exists

1. Docker vs Traditional Setup

Traditional:                Docker:
+--------------------+      +--------------------+
| App                |      | App + Dependencies  |
| Dependencies       |      | (one tiny layer)    |
| OS (Ubuntu 20.04)  |      | Host OS (any Linux) |
| Hardware           |      | Docker Engine       |
+--------------------+      +--------------------+

Docker removes the "guest OS" → 100x lighter than VMs.

Docker Architecture – What Runs Where?

+-----------------+
| Docker Desktop  | ← GUI for Windows/Mac
| Docker CLI      | ← You type commands
+-----------------+
        ↓
+-----------------+
| Docker Daemon   | ← The brain (dockerd)
| (Docker Engine) |
+-----------------+
        ↓
Linux Kernel (cgroups, namespaces) ← Real magic happens here

Key Components

Component Job
Docker CLI docker run, docker ps
Docker Daemon Manages containers, images, networks
Docker Desktop Runs Linux VM on Windows/Mac
containerd Lightweight runtime (replaced Docker shim)
runc Actually starts containers

2. Docker Under the Hood: Linux Kernel Magic (cgroups + namespaces)

How Docker Creates "Lightweight VMs" Without a Hypervisor

Docker doesn't invent isolation — it uses Linux kernel features
Namespaces = "What the container sees"
cgroups = "What the container can use"

Think of it like putting your app in a private apartment: - Namespaces → give it its own view of the building (files, network, users) - cgroups → give it a limited budget (CPU, RAM, electricity)

Let’s break it down.

1. Namespaces – "Private Apartments for Processes"

Linux has 7 types of namespaces. Docker uses 6 of them.

Namespace What It Isolates Docker Example
PID Process IDs Container sees pid 1 as its init, not host's
NET Network interfaces, IP, ports Container gets 172.17.0.2, can't see host Wi-Fi
MNT Filesystem mount points / inside container ≠ host /
UTS Hostname & domain Container can be named web-01
IPC Inter-process communication shm only shared within container
User User & group IDs root in container ≠ real root (UID 1000)

Real Example: PID Namespace

# On host
ps aux | grep nginx
# → PID 1234 (real system PID)

# Inside container
docker exec mynginx ps aux
# → PID 1  → nginx (thinks it's the only process!)

No other container or host can see its PIDs.

2. cgroups (Control Groups) – "Resource Budget Manager"

cgroups = Linux's way to limit, account, and isolate resource usage (CPU, memory, disk I/O, network).

Docker uses cgroups to prevent one container from starving others.

Key cgroups Docker Uses

Resource cgroup Controller Docker Flag
CPU cpu --cpus=1.5
Memory memory --memory=512m
Disk I/O blkio --device-read-bps
PIDs pids --pids-limit=100

Real Example: Memory Limit

docker run -d --name stress --memory=100m progrium/stress
docker exec stress stress --vm 1 --vm-bytes 200m

→ Container crashes with OOM (Out of Memory)
→ Host stays safe → no swap thrashing

How Docker Combines Them

+--------------------------------------------------+
|               Docker Container                   |
|  +-------------------------------------------+   |
|  |  App (nginx)                              |   |
|  |  PID: 1                                   |   |
|  |  IP: 172.17.0.3                           |   |
|  |  Files: /var/www (from image)             |   |
|  +-------------------------------------------+   |
|          ↑               ↑                       |
|     Namespaces       cgroups                     |
|   (isolation)       (resource limits)            |
+--------------------------------------------------+
          ↑                  ↑
      Linux Kernel     Linux Kernel

Deep Dive: What Happens When You Run docker run nginx

Step Kernel Feature What Happens
1 MNT namespace Mounts overlay filesystem (image layers)
2 PID namespace New PID space; nginx becomes PID 1
3 NET namespace Creates veth pair → container gets private IP
4 UTS namespace Sets container hostname
5 User namespace Maps root → unprivileged host UID
6 cgroup Sets memory.limit=∞, cpu.shares=1024
7 runc Executes /bin/nginx in this isolated world

Analogy: Hotel vs Docker

Hotel Room Docker Container
You see only your room Namespaces
You get 1 lamp, 1 bed cgroups
Key card opens only your door Isolation
Hotel manager sets power limit Resource control

Why This Matters (Real-World Impact)

Scenario Without cgroups/namespaces With Docker
Noisy neighbor app Crashes entire server Limited to 512MB RAM
Security breach Attacker sees all processes Sees only container
Port conflict Two apps want port 80 Each has own lo interface
Dev → Prod "Works on my machine" Identical environment
# 1. Check container's view
docker run -it ubuntu bash
# → hostname, ip, ps → all isolated

# 2. On host, inspect cgroups
cat /sys/fs/cgroup/memory/$(docker inspect --format '{{.Id}}' <container>)/memory.limit_in_bytes
# → 9223372036854771712 (unlimited) or your --memory value

# 3. See network namespace
sudo nsenter -t $(docker inspect --format={{.State.Pid}} <container>) -n ip addr
# → Shows container's eth0@...

Summary: The Magic Formula

Docker Container = 
    Namespaces (6 types) 
  + cgroups (resource limits)
  + OverlayFS (image layers)
  + Linux Kernel

No VM. No hypervisor. Just smart use of Linux.

Docker is not a VM. It's a process with superpowers.

Now you know why Docker is fast, secure, and consistent — it's all Linux kernel magic.
Next step: Try docker run --cpus=0.5 --memory=100m nginx and watch it get throttled!

3. Docker vs VMware (Virtual Machines)

Feature Docker Container VMware VM
Size 50–200 MB 10–50 GB
Boot time 1–2 seconds 30–60 seconds
OS Shares host kernel Full guest OS
Isolation Process-level (namespaces) Hardware-level (hypervisor)
Performance Near-native 5–15% overhead
Density 1000+ per host 10–50 per host
Use case Microservices, CI/CD Different OS, legacy apps

Docker = lightweight process
VM = heavy computer inside computer


4. Hypervisor vs Docker Engine

Type Hypervisor (Type 1/2) Docker Engine
Examples VMware ESXi, Hyper-V, VirtualBox Docker, Podman
Runs on Bare metal or host OS Linux kernel
Isolates Full machines Processes
Needs CPU virtualization (VT-x/AMD-V) Just Linux kernel

Docker does NOT use a hypervisor (except on Windows/Mac via tiny Linux VM).


5. Docker Desktop – What It Actually Does

Windows/Mac users → Your machine doesn’t have Linux kernel
Docker Desktop creates a tiny Linux VM (2–4GB) using: - Windows → Hyper-V or WSL2 - Mac → HyperKit or Virtualization.framework

Your Mac/Windows
       ↓
Docker Desktop creates Linux VM
       ↓
Docker Engine runs inside it
       ↓
You run containers

Docker Desktop is NOT Docker – it’s just a launcher for non-Linux machines.


6. Basic Docker Commands (Must Know)

# Images
docker pull nginx               # Download image
docker images                   # List images
docker build -t myapp .         # Build from Dockerfile

# Containers
docker run nginx                # Run container
docker run -d -p 8080:80 nginx  # Detached + port mapping
docker ps                       # Running containers
docker ps -a                    # All containers
docker logs mycontainer         # View logs
docker exec -it mycontainer bash # Enter container

# Cleanup
docker stop mycontainer
docker rm mycontainer
docker rmi nginx                # Remove image

7. Docker Volumes – Persistent

"Where does my database data go?"

Containers are ephemeral → delete container = delete data

3 Ways to Persist Data

# 1. Bind mount (your folder)
docker run -v /home/user/data:/app/data nginx

# 2. Named volume (Docker manages)
docker volume create mydata
docker run -v mydata:/app/data postgres

# 3. Anonymous volume
docker run -v /app/data postgres

Volume Types Compared

Type Path Known? Managed by Docker? Use Case
Bind mount Yes No Edit code live
Named volume No Yes Databases
Anonymous No Yes Temp data

8. Docker Networks – How Containers Talk

By default, each container gets its own isolated network stack (thanks to Linux NET namespace).
→ Without configuration, containers cannot talk to each other or the outside world.

Docker provides 5 built-in network drivers to control how and when containers communicate.

The 5 Docker Network Types

Network Type Command Isolation External Access Use Case
bridge Default Yes With -p Most apps
host --network host No Direct High perf
none --network none Full No Security
overlay Swarm mode Yes With ingress Multi-host
macvlan --network macvlan Yes Direct IP Legacy integration

1. bridge – The Default & Most Used

Host (192.168.1.100)                  Container
┌────────────────────┐              ┌──────────────┐
│ docker0 bridge     │ ──veth──►   │ eth0         │
│ 172.17.0.1         │              │ 172.17.0.2   │
└────────────────────┘              └──────────────┘

Custom Bridge Network (Best Practice)

docker network create --driver bridge mynet
docker run --network mynet --name db postgres
docker run --network mynet --name app myapp

appdb:5432

2. host – Zero Isolation, Max Performance

docker run --network host nginx

→ nginx listens on host’s port 80 directly

Pros - Zero overhead (no NAT, no veth) - Full access to host interfaces - Ideal for monitoring agents, network tools

Cons - No isolation → security risk - Port conflicts → only one service per port - Not portable

Real-World Use

# Prometheus Node Exporter
docker run --network host --pid host prom/node-exporter

3. none – Total Isolation

docker run --network none alpine

What You Get

Use Cases

docker run --network none myapp process-data /input /output

4. overlay – Multi-Host Networking (Docker Swarm)

Node 1                  Node 2
┌────────────┐        ┌────────────┐
│ web:10.0.0.2│ ◄──► │ db:10.0.0.3│
└────────────┘        └────────────┘
     overlay network (10.0.0.0/24)

Setup

docker swarm init
docker network create --driver overlay mynet
docker service create --network mynet --name web nginx

Use Case - Microservices across 10 servers - Zero-downtime deployments

5. macvlan – Give Container Its Own MAC & IP

docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 my_macvlan

docker run --network my_macvlan --ip=192.168.1.100 myapp

Security Best Practices

Rule Why
Use internal: true for DB networks Prevent external access
Never use host in production Breaks isolation
Use --network-alias for DNS api.service.local
Limit with iptables Block unused ports

Performance Comparison

Network Latency Throughput CPU Overhead
bridge ~0.1ms High Low
host 0ms Max None
overlay ~1–2ms Medium Medium
macvlan ~0.1ms High Low

Why Port Forwarding (-p host:container) is Needed in Docker?

Because each container has its own private network (thanks to Linux NET namespace).
Without -p, the container’s port is only reachable inside the containernot from your host or the internet.

Layer Network View
Host (your laptop/server) Has IP: 192.168.1.100, listens on port 80
Container Has private IP: 172.17.0.2, listens on port 80

→ Container’s 80 ≠ Host’s 80
→ They are in different network namespaces

Host Network             Container Network
┌───────────────┐       ┌─────────────────┐
│ IP: 192.168.1.100     │ IP: 172.17.0.2     │
│ Port 80: [free] ─────►│ Port 80: [nginx]   │
└───────────────┘  -p  │                 │
                    8080:80               │
                    └─────────────────┘

What -p 8080:80 Actually Does

docker run -p 8080:80 nginx
Part Meaning
8080 Host port – what you type in browser
80 Container port – where nginx is listening
-p Tell Docker: "forward traffic from host:8080 → container:80"

Docker uses iptables (or nftables) to set up NAT (Network Address Translation):

Incoming packet:
  DST: 192.168.1.100:8080
        ↓ (DNAT)
  DST: 172.17.0.2:80
        ↓
  Reaches nginx → 200 OK

9. Docker Compose

docker-compose.yml = one file to rule them all

Sample docker compose file which deploys multiple services/containers running on multiple networks with mulitple volume mounts etc. Compose file

docker-compose up -d    # Start everything
docker-compose down       # Stop and remove

10. Docker Container vs Host: What Is Shared?

Resource Shared? Details
Linux Kernel Yes Containers use the same kernel as the host (via namespaces)
CPU Yes Same physical CPU cores (time-sliced via cgroups)
Memory (RAM) Yes Same physical RAM (limited by cgroups)
Network Stack Partially Host kernel handles packets; container has virtual interfaces (veth)
Storage Driver Yes overlay2, aufs, etc. run on host kernel
Binaries & Libraries No (unless bind mount) Container has its own filesystem (from image)
Processes No Isolated via PID namespace
Filesystem No (unless volume) Container has its own root (/)
Users No (unless --user) Isolated via user namespace
Devices No (unless --device) GPU, USB, etc. blocked by default

How Isolation Works (Even When Shared)

Shared Resource Isolation Mechanism
Kernel Namespaces (PID, NET, MNT, UTS, IPC, User)
CPU/RAM cgroups (limits usage)
Network Network namespace + iptables/NAT
Filesystem OverlayFS / UnionFS (image layers + writable top layer)

Real Example: What You See

# On Host
uname -r
# → 5.15.0-100-generic

# Inside Container
docker run -it alpine uname -r
# → 5.15.0-100-generic  ← SAME KERNEL!
# CPU is shared
htop  # → See container processes using host CPU

What You Can Access from Container (by default)

Resource Accessible? How
Host files No Only via -v /host/path:/container/path
Host processes No ps aux shows only container PIDs
Host network No ip a shows container IPs (172.17.x.x)
Host devices No Only with --device /dev/nvidia0

Bottom Line:

Docker containers share the host kernel and hardware — but are isolated at the process, network, and filesystem level.
It’s not a VM — it’s a securely jailed process.

Summary Cheat Sheet

Concept One-Liner
Docker Run apps in isolated, portable boxes
Docker Daemon Background service that manages everything
Docker Desktop GUI + Linux VM for Windows/Mac
VM vs Docker VM = full OS, Docker = just app
Hypervisor VMware/Hyper-V (not used by Docker)
Volumes Persist data outside containers
Networks Let containers talk to each other
Docker Compose docker-compose up = full app stack

Master these 8 concepts → you’re a Docker pro.

Now go run:

docker run -d -p 8080:80 nginx

And open http://localhost:8080 – welcome to the future!

Docker Images

A Docker image is a read-only template that contains: - Your app code - Dependencies (libraries, binaries) - OS filesystem (just enough to run the app) - Configuration (env vars, entrypoint)

Think of it as "a frozen, executable snapshot of your app".

Your App (Python, Node.js, Java)
       ↓
Docker Image = App + OS + Dependencies
       ↓
Container = Running instance of the image

2. Image vs Container – The Key Difference

Feature Docker Image Docker Container
State Read-only (immutable) Writable (running instance)
Lifecycle Created once, reused Created from image, can be stopped
Storage Layers in /var/lib/docker Adds a writable layer on top
Example nginx:1.25 docker run nginx:1.25 → container
Analogy Blu-ray disc DVD player playing the movie
docker images          # List images (Blu-rays)
docker ps              # List running containers (players)

3. How Images Are Built – The Dockerfile

Dockerfile = recipe to build an image

# Dockerfile
FROM node:18-alpine           # Base image
WORKDIR /app                  # Set working directory
COPY package*.json .          # Copy dependency files
RUN npm ci --only=production  # Install deps
COPY . .                      # Copy app code
EXPOSE 3000                   # Document port
CMD ["node", "server.js"]     # Default command

4. Dockerfile Commands – Full Breakdown

Command What It Does Example
FROM Base image – start from here FROM ubuntu:22.04
WORKDIR Set working directory WORKDIR /app
COPY Copy files from host → image COPY . .
ADD Like COPY, but can untar or fetch URLs ADD app.tar.gz /app/
RUN Execute command during build RUN apt update && apt install -y curl
CMD Default command when container starts CMD ["python", "app.py"]
ENTRYPOINT Fixed command (hard to override) ENTRYPOINT ["nginx"]
ENV Set environment variable ENV NODE_ENV=production
EXPOSE Document port (does NOT publish) EXPOSE 8080
VOLUME Declare persistent storage VOLUME /data
USER Run as specific user USER node
LABEL Add metadata LABEL maintainer="you@example.com"
ARG Build-time variable ARG VERSION=1.0

5. Image Layers – How Docker Saves Space

Every Dockerfile instruction = one layer

Layer 1: FROM node:18-alpine
Layer 2: WORKDIR /app
Layer 3: COPY package*.json .
Layer 4: RUN npm ci
Layer 5: COPY . .
Layer 6: CMD ["node", "server.js"]

Layer Caching (The Magic)

docker build -t myapp .

Best Practice: Put rarely changing steps early

# GOOD: Install deps first
COPY package*.json .
RUN npm ci
COPY . .
# BAD: Copy code first
COPY . .
RUN npm ci        # Re-runs on every code change!

6. How to Optimize Docker Images

1. Use Small Base Images

# 1.8 GB → 80 MB
FROM ubuntu:22.04       # BAD
FROM alpine:3.19        # GOOD
FROM node:18-alpine     # BEST for Node.js

2. Multi-Stage Builds (Production Secret)

Multi-stage Docker build optimizes images by:

  1. Separating build and runtime
  2. Stage 1 (Build): Use heavy tools (compilers, SDKs)
  3. Stage 2 (Runtime): Use tiny base (e.g., alpine) — no build tools

  4. Copy only needed files
    dockerfile COPY --from=builder /app/dist /app → Final image has only compiled binaries, not source or build deps

  5. Dramatically reduces size
    Before: 900 MB (Node.js + source + npm) After: ~80 MB (just compiled JS + runtime)

  6. Improves security & speed

  7. No unused tools = smaller attack surface
  8. Faster pull/push/scans

Result: 10x smaller, faster, safer images — production standard.

# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Run
FROM alpine:3.19
RUN apk add --no-cache nodejs
COPY --from=builder /app/dist /app
CMD ["node", "/app/server.js"]

→ Final image: ~50 MB (no build tools!)

3. .dockerignore (Like .gitignore)

node_modules
.git
*.log
dist

→ Speeds up COPY, reduces image size

4. Combine RUN Commands

# BAD: 3 layers
RUN apt update
RUN apt install -y curl
RUN rm -rf /var/lib/apt/lists/*

# GOOD: 1 layer
RUN apt update && \
    apt install -y curl && \
    rm -rf /var/lib/apt/lists/*

8. Image Tagging & Registry

# Tag image
docker build -t myapp:1.0 .
docker tag myapp:1.0 myusername/myapp:1.0

# Push to Docker Hub
docker push myusername/myapp:1.0

# Pull from anywhere
docker pull myusername/myapp:1.0

9. Fundamental Concepts

Concept Explanation
Image ID Unique hash (sha256:abc123...)
Registry Docker Hub, GitHub Container Registry, AWS ECR
Repository Collection of tagged images (nginx, nginx:1.25)
Layer Deduplication Same layer (e.g., alpine:3.19) shared across images
Union Filesystem overlay2 merges layers into one filesystem
Image Squashing docker squash (experimental) → flatten layers

10. Commands Cheat Sheet

# Build
docker build -t myapp:1.0 .

# List images
docker images

# Inspect image
docker inspect myapp:1.0

# View layers
docker history myapp:1.0

# Remove image
docker rmi myapp:1.0

# Save/load image (offline)
docker save myapp:1.0 > myapp.tar
docker load < myapp.tar

Summary Table

Concept Key Point
Image Read-only template
Container Running, writable instance
Dockerfile Recipe to build image
Layers Each command = 1 layer
Caching Unchanged layers reused
Multi-stage Build → discard tools → tiny image
Optimization Small base + .dockerignore + combine RUN

Golden Rule:

"Build once, run anywhere"
One image = runs identically on your laptop, AWS, GCP, Azure, or your friend’s PC.