Welcome to the Complete Docker Notes. This guide is designed to teach you Docker from a beginner to an advanced level using practical examples, diagrams, tables, commands, and real development workflows.
Whether you are running Linux, Windows, or macOS, this handbook covers the exact steps, syntax differences, and architectural variations you need to know.
- What is Docker?
- Architecture Differences
- Docker Desktop vs Docker Engine
- Complete Installation Guide
- Running Docker Without Sudo
- Filesystem and Volume Differences
- Running Containers
- Writing Dockerfiles and Creating Images
- Apple Silicon and Multi-Platform Images
- Docker Compose and Multi-Container Apps
- Networking and Environment Variables
- Logs, Debugging, and Health Checks
- Security and Optimizing Images
- Cleaning Docker Resources Safely
- Troubleshooting
Docker is a platform that allows you to package an application and all its dependencies into a standardized unit called a container. Containers are lightweight, isolated environments that run consistently across any machine.
- Image: A read-only template with instructions for creating a Docker container (like a blueprint).
- Container: A runnable instance of an image.
- Volume: A persistent storage mechanism for container data.
- Network: A communication layer for containers to talk to each other.
How Docker runs depends entirely on your operating system.
Docker runs directly on the Linux kernel. It uses namespaces and cgroups provided by the kernel to isolate processes. This is the most native and performant way to run Docker.
Windows uses Docker Desktop, which relies on WSL 2 (Windows Subsystem for Linux) or Hyper-V to run a lightweight Linux virtual machine. The containers run inside this WSL 2 backend, feeling seamless to the user.
macOS also uses Docker Desktop. Since macOS is Unix but not Linux, Docker runs a lightweight Linux VM in the background (using Hypervisor.framework) to execute Linux containers.
graph TD
subgraph Linux OS
kernel[Linux Kernel]
C1[Container 1]
C2[Container 2]
kernel --> C1
kernel --> C2
end
subgraph Windows / macOS
host[Host OS]
vm[Lightweight Linux VM / WSL2]
WC1[Container 1]
WC2[Container 2]
host --> vm
vm --> WC1
vm --> WC2
end
When installing Docker, you have two main choices:
| Feature | Docker Engine | Docker Desktop |
|---|---|---|
| Interface | CLI only | GUI + CLI |
| Availability | Linux | Windows, macOS, Linux |
| Core Tech | Direct kernel access | Virtual Machine / WSL 2 |
| Resource Limits | Set via Linux cgroups | Configured via GUI settings |
| Kubernetes | Needs external tools (Minikube/K3s) | Built-in (1-click install) |
| Extensions | No | Yes |
| Licensing | Free / Open Source | Free for personal/small business, Paid for large enterprises |
Below are the workflows to install Docker across all major operating systems. Note: Always verify commands with official documentation, as repository URLs can update.
- Starting situation: Fresh Fedora install.
- Commands:
sudo dnf -y install dnf-plugins-core sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker - Expected result: Docker service is running and enabled on boot.
- Starting situation: Fresh Ubuntu install.
- Commands:
# Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker
- Starting situation: Fresh Debian install.
- Commands: Follow the exact same commands as Ubuntu, but the script automatically detects the Debian codename.
- Starting situation: Fresh Arch install.
- Commands:
sudo pacman -S docker docker-compose docker-buildx sudo systemctl enable --now docker
- Starting situation: Fresh Manjaro install.
- Commands: Identical to Arch Linux (
sudo pacman -S docker docker-compose docker-buildx).
- Starting situation: Fresh openSUSE install.
- Commands:
sudo zypper install docker docker-compose python3-docker-compose sudo systemctl enable --now docker
- Starting situation: Fresh RHEL system.
- Commands:
sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker
- Commands: Identical to RHEL setup.
- Commands: Identical to RHEL setup.
- Commands: Identical to RHEL setup.
- Starting situation: Fresh Linux Mint install.
- Commands: Linux Mint is based on Ubuntu. Use the Ubuntu installation workflow.
- Starting situation: Fresh Pop!_OS install.
- Commands: Pop!_OS is based on Ubuntu. Use the Ubuntu installation workflow.
- Starting situation: Fresh Kali Linux install.
- Commands: Kali Linux is based on Debian. Use the Debian installation workflow.
| Task | Fedora | Ubuntu/Debian | Arch/Manjaro | openSUSE | RHEL/Rocky/Alma/CentOS |
|---|---|---|---|---|---|
| Install Command | sudo dnf install docker-ce... |
sudo apt-get install docker-ce... |
sudo pacman -S docker... |
sudo zypper install docker... |
sudo yum install docker-ce... |
| Start Service | sudo systemctl start docker |
sudo systemctl start docker |
sudo systemctl start docker |
sudo systemctl start docker |
sudo systemctl start docker |
- Starting situation: Windows 10 Pro, Enterprise, or Home (Build 19044+).
- Commands / Steps:
- Install WSL 2 by opening PowerShell as Administrator and running:
wsl --install - Restart the computer.
- Download and install Docker Desktop for Windows from the official website.
- Ensure "Use WSL 2 instead of Hyper-V" is checked in Docker Desktop settings.
- Install WSL 2 by opening PowerShell as Administrator and running:
- Expected result: Docker icon appears in system tray, and
docker versionworks in PowerShell.
- Starting situation: Windows 11 system.
- Commands: Same exact steps as Windows 10 using WSL 2.
- Notes: Docker Desktop is not supported on Windows Server. You must install the Docker EE (Enterprise Edition) or use the Mirantis Container Runtime. Alternatively, use WSL 2 natively on Server 2022 if configured for container development.
- Starting situation: Mac with Intel processor.
- Steps:
- Download Docker Desktop for Mac (Mac with Intel chip).
- Double-click the
.dmgand drag Docker to Applications. - Open Docker from Applications to complete setup.
- Starting situation: Mac with M1/M2/M3/M4 chip.
- Steps:
- Download Docker Desktop for Mac (Mac with Apple chip).
- Install Rosetta 2 if required for older x86 tools:
/usr/sbin/softwareupdate --install-rosetta --agree-to-license - Drag Docker to Applications and open it.
- Enable "Use Rosetta for x86/amd64 emulation" in Docker Desktop Settings -> General for better performance when running Intel images.
By default, the Docker daemon binds to a Unix socket owned by the root user.
To run Docker without typing sudo every time:
sudo usermod -aG docker $USER
newgrp docker- Explanation: Adds your current user to the
dockergroup.
You do not need sudo or administrator privileges to run Docker commands on Windows and macOS. Docker Desktop manages the permissions and user access to the Docker daemon automatically.
Bind mounts allow you to map a folder on your host machine to a folder inside the container. Because operating systems format paths differently, the commands change depending on your OS and terminal.
| OS | Example Path |
|---|---|
| Linux | /home/user/project |
| Windows | C:\Users\User\project |
| macOS | /Users/user/project |
docker run -v "$(pwd)":/app my-imagedocker run -v "${PWD}:/app" my-imagedocker run -v "%cd%:/app" my-image- Note: On Windows, Docker Desktop automatically translates Windows paths (
C:\...) to Unix paths for the container.
To run a basic container:
docker run -d --name web -p 8080:80 nginx-d: Detached mode (runs in background).--name: Names the container.-p 8080:80: Maps port 8080 on your host to port 80 inside the container.
- List running containers:
docker ps - List all containers:
docker ps -a - Stop a container:
docker stop web - Remove a container:
docker rm web - Execute a shell inside a container:
docker exec -it web /bin/sh
A Dockerfile is a text document that contains the commands to assemble an image.
# Use an official runtime as a parent image
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy package.json and install dependencies
COPY package*.json ./
RUN npm install
# Copy the rest of the application
COPY . .
# Expose port and start app
EXPOSE 3000
CMD ["npm", "run", "start"]Build the image:
docker build -t my-node-app .Apple Silicon (M1/M2/M3/M4) uses the ARM64 architecture. Most traditional servers use AMD64 (x86_64).
If you build an image on an M1 Mac, it is ARM64 by default. If you deploy it to an AWS EC2 Intel instance, it will fail.
Use docker buildx to build for specific architectures.
Build for AMD64 (from an Apple Silicon Mac):
docker buildx build --platform linux/amd64 -t my-app-amd64 .Build for ARM64:
docker buildx build --platform linux/arm64 -t my-app-arm64 .Docker Desktop on Mac uses Rosetta 2 to emulate AMD64 images, but running native ARM64 images is always faster.
Docker Compose is a tool for defining and running multi-container applications using a docker-compose.yml file.
version: '3.8'
services:
api:
build: ./api
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/appdb
- REDIS_URL=redis://redis:6379/0
- MONGO_URL=mongodb://root:example@mongodb:27017/
- KAFKA_BROKER=kafka:9092
depends_on:
- db
- redis
- mongodb
- kafka
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
mongodb:
image: mongo:6-jammy
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
volumes:
- mongodata:/data/db
kafka:
image: bitnami/kafka:latest
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
volumes:
pgdata:
mongodata:Commands:
- Start stack:
docker compose up -d - Stop stack:
docker compose down
Note: On older installations, the command was docker-compose (with a hyphen). The modern standard is docker compose (with a space).
Containers on the same Docker network can talk to each other using their service names as hostnames.
In the YAML above, the api service connects to the database using the hostname db (postgresql://user:pass@db:5432/appdb).
You can pass environment variables in several ways:
- Direct in Docker Compose (
environment:block) - Using an
.envfile andenv_file: .env - Command line:
docker run -e DB_USER=admin nginx
- Container logs:
docker logs <container_name> - Follow logs in real-time:
docker logs -f <container_name> - Docker Compose logs:
docker compose logs -f
Ensure a container is actually ready to receive traffic, not just "running".
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d appdb"]
interval: 10s
timeout: 5s
retries: 5- Use Alpine or Minimal Base Images: e.g.,
python:3.11-alpineornode:20-slim. - Don't run as Root:
RUN adduser -D myuser USER myuser
- Use Multi-Stage Builds: Compile in one stage, copy binary to a clean, small stage.
- .dockerignore: Always include a
.dockerignorefile (like.gitignore) to excludenode_modules,.git, and environment variables files from your image context.
Docker can consume gigabytes of disk space over time.
- Remove unused containers, networks, images (dangling):
docker system prune
- Remove EVERYTHING unused (including stopped containers and unused volumes):
docker system prune -a --volumes
- Linux Fix:
sudo systemctl restart docker - Windows Fix: Open Docker Desktop application. Ensure WSL 2 is installed (
wsl --update). - macOS Fix: Open Docker Desktop application from Applications folder.
- Linux Fix: Run
sudo usermod -aG docker $USERand log out/log back in. - Windows/macOS Fix: Ensure Docker Desktop is actually running.
- Linux Fix: Check SELinux contexts (Fedora/RHEL). Add
:Zto the end of the mount:-v $(pwd):/app:Z. - Windows Fix: Check paths. Use PowerShell
${PWD}instead of$(pwd). Ensure File Sharing is enabled in Docker Desktop. - macOS Fix: Ensure Docker Desktop has permission to access the folder (Settings -> Resources -> File sharing).
- Fix: You built an ARM64 image on an Apple Silicon Mac, but deployed it to an AMD64 server. Rebuild with
--platform linux/amd64.
- Windows Fix: Open an elevated PowerShell and run:
Ensure Virtualization is enabled in your BIOS/UEFI.
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart wsl --set-default-version 2
- Linux Fix: Edit
/etc/docker/daemon.jsonand add{"dns": ["8.8.8.8", "8.8.4.4"]}, then restart Docker. - Windows/macOS Fix: Change DNS settings in the host OS or configure within the Docker Desktop settings GUI under Network.
End of Guide