Docker is an open-source platform that lets you package an application and all its dependencies into a single unit called a container. That container can run on any machine that has Docker installed — no "it works on my machine" problems.
- Consistency — The app behaves the same in development, staging, and production.
- Isolation — Each container runs independently without interfering with others.
- Portability — Build once, run anywhere (Linux, Windows, Mac, cloud).
- Speed — Containers start in seconds, unlike virtual machines.
- Efficiency — Containers share the host OS kernel, so they use far less memory and disk space than VMs.
| Feature | Virtual Machine (VM) | Docker Container |
|---|---|---|
| Boots | Full OS (minutes) | App process (seconds) |
| Size | GBs | MBs |
| Isolation | Hardware-level | Process-level |
| OS | Each VM has its own OS | Shares host OS kernel |
| Performance | Slower (overhead) | Near-native speed |
| Use case | Full OS isolation needed | App packaging & microservices |
In short: VMs virtualise the entire hardware stack. Docker virtualises only the application layer on top of a shared OS kernel — making it much lighter and faster.
VM Stack Docker Stack
┌──────────────────┐ ┌──────────────────┐
│ Application │ │ Application │
│ Guest OS │ │ Dependencies │
│ Hypervisor │ │ Docker Engine │
│ Host OS │ │ Host OS │
│ Hardware │ │ Hardware │
└──────────────────┘ └──────────────────┘
Docker containers always run Linux processes internally. How that is achieved depends on your OS.
- Docker runs natively — containers share the Linux host kernel directly.
- No virtualisation layer needed. Best performance.
- macOS has no Linux kernel, so Docker Desktop runs a lightweight Linux VM (using Apple Hypervisor Framework) behind the scenes.
- Your containers run inside that VM. Docker Desktop hides this complexity.
- Two modes available:
- WSL 2 (recommended) — Docker Desktop uses Windows Subsystem for Linux 2, which runs a real Linux kernel inside Windows. Fast and lightweight.
- Hyper-V — Docker runs a Linux VM using Hyper-V. Older approach, more overhead.
macOS / Windows
┌─────────────────────────────┐
│ Docker Desktop │
│ ┌───────────────────────┐ │
│ │ Linux VM / WSL2 │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Container │ │ │
│ │ └─────────────────┘ │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
# Update package index
sudo apt update
# Install Docker
sudo apt install -y docker.io
# Start and enable Docker
sudo systemctl start docker
sudo systemctl enable docker
# Run without sudo (optional)
sudo usermod -aG docker $USER
# Verify
docker --version- Download Docker Desktop from docker.com
- Open the
.dmgfile and drag Docker to Applications - Launch Docker Desktop from Applications
- Verify in terminal:
docker --version- Enable WSL 2 — open PowerShell as Administrator:
wsl --install- Download Docker Desktop from docker.com
- Run the installer — make sure "Use WSL 2" is checked
- Restart your PC
- Verify in PowerShell:
docker --versionThis project is a simple Node.js + Express app containerised with Docker.
l2-M-53/
├── index.js
├── package.json
└── Dockerfile
FROM node:20-alpine # Use lightweight Node.js 20 base image
WORKDIR /usr/src/app # Set working directory inside container
COPY package.json ./ # Copy dependency list
RUN npm install --production # Install dependencies
COPY . . # Copy all source files
EXPOSE 5000 # Declare the port the app listens on
CMD ["node", "index.js"] # Command to start the appStep 1 — Build the image
docker build -t my-express-app .-t my-express-appgives the image a name (tag).means use the Dockerfile in the current directory
Step 2 — Run a container from the image
docker run -p 5000:5000 my-express-app-p 5000:5000maps port 5000 on your machine to port 5000 inside the container
Step 3 — Open in browser
http://localhost:5000
You should see: Hello World!
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# List all images
docker images
# Stop a container
docker stop <container_id>
# Remove a container
docker rm <container_id>
# Remove an image
docker rmi my-express-app