Docker
Build, Ship, and Run Containers Anywhere
What is Docker?
Docker is a containerization platform that packages applications and their dependencies into lightweight, portable containers. Unlike virtual machines, containers share the host OS kernel, making them faster to start and more resource-efficient. Docker provides tools for building images, running containers, and orchestrating multi-container applications.
Think of Docker like shipping containers for software
Before shipping containers, loading cargo was slow and error-prone - different goods needed different handling. Shipping containers standardized everything. Docker does the same for software: your app runs the same way everywhere, regardless of the underlying infrastructure.
Key Features
Lightweight Containers
Share host OS kernel. Start in seconds, not minutes like VMs.
Consistent Environments
Works on my machine = works everywhere. Dev/prod parity.
Docker Hub Registry
Pull official images for any stack. Push your own images.
Docker Compose
Define multi-container apps in YAML. One command deployment.
When to Use
- Consistent development environments across teams
- Microservices architecture
- CI/CD pipelines - build once, deploy anywhere
- Local development matching production
- Isolating application dependencies
- Running multiple versions of software simultaneously
When Not to Use
- GUI applications (containers are primarily for headless apps)
- When you need full OS isolation (use VMs instead)
- Extremely performance-sensitive workloads (small overhead exists)
- Persistent data storage (use volumes or external storage)
- Windows-only applications on Linux hosts (and vice versa)
Prerequisites
- Basic command line knowledge
- Understanding of Linux basics (helpful)
- Sufficient disk space for images (~20GB recommended)
Installation
curl -fsSL https://get.docker.com | shOfficial install script
sudo apt install docker.ioUbuntu/Debian package
brew install --cask dockerDocker Desktop
winget install Docker.DockerDesktopDocker Desktop
Verify installation: docker --version && docker run hello-world
Quick Start Steps
Verify installation
Run the hello-world container to verify Docker works
docker run hello-world
Run your first container
Start an nginx web server container
docker run -d -p 8080:80 nginx # Visit http://localhost:8080
Create a Dockerfile
Build your own image from a Node.js app
Build and run your image
Build the image and run it as a container
docker build -t my-app . docker run -d -p 3000:3000 my-app
docker run <image>Create and start a container from image
docker psList running containers
docker ps -aList all containers (including stopped)
docker imagesList local images
docker pull <image>Download image from registry
docker build -t <name> .Build image from Dockerfile
docker stop <container>Stop a running container
docker rm <container>Remove a container
docker rmi <image>Remove an image
docker logs <container>View container logs
docker exec -it <container> shExecute command in container
docker-compose up -dStart services defined in compose file
Commands by Category
Pro Tips14
Optimize layer caching
performanceOrder Dockerfile instructions from least to most frequently changing. Put COPY package*.json and RUN npm install before COPY . . This way, dependencies are cached unless package.json changes.
Use Alpine-based images
performanceAlpine Linux images are ~5MB vs ~100MB+ for Debian-based. Use node:20-alpine, python:3.11-alpine, etc. Only use full images if you need specific libraries.
FROM node:20-alpineFROM node:20Run as non-root user
securityCreate and use a non-root user in your Dockerfile. Running as root in containers is a security risk if the container is compromised.
One process per container
best-practiceEach container should do one thing well. Don't run multiple services in one container. Use Docker Compose for multi-service applications.
Use specific image tags
best-practiceAlways specify image versions in production. :latest can change unexpectedly. Pin to specific versions like node:20.10-alpine.
FROM node:20.10-alpineFROM node:latestUse multi-stage builds
performanceMulti-stage builds let you use build tools without including them in the final image. Dramatically reduces image size and attack surface.
Always add health checks
best-practiceHEALTHCHECK instructions tell orchestrators if your app is actually working, not just running. Essential for production reliability.
Use ENV for runtime config, ARG for build-time
gotchaARG values are only available during build. ENV values persist into the running container. Use ARG for build secrets that shouldn't be in the final image.
Combine RUN commands
performanceEach RUN creates a layer. Combine related commands with && to reduce layers and image size. Clean up in the same RUN that creates files.
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*RUN apt-get update\nRUN apt-get install -y curl\nRUN rm -rf /var/lib/apt/lists/*Prefer COPY over ADD
best-practiceCOPY is transparent and predictable. ADD has extra features (tar extraction, URL fetching) that are rarely needed and can be confusing.
Use .dockerignore
performanceA .dockerignore file speeds up builds by excluding unnecessary files (node_modules, .git, docs). Similar to .gitignore syntax.
Prune unused resources
automationDocker accumulates unused images, containers, and volumes. Run 'docker system prune' periodically. Use '-a' flag to remove all unused images.
Use named volumes over bind mounts in production
best-practiceNamed volumes are managed by Docker and portable. Bind mounts depend on host filesystem paths. Use bind mounts for development, named volumes for production.
Scan images for vulnerabilities
securityUse 'docker scout' or Trivy to scan images for known vulnerabilities. Integrate into CI/CD pipelines. Keep base images updated.
Key Facts10
Containers share the host OS kernel, VMs have their own OS
This makes containers much lighter (~MB) than VMs (~GB), and they start in seconds instead of minutes.
Docker images are built from layers that are cached and reused
Each instruction in a Dockerfile creates a layer. Layers are cached and shared between images, saving disk space and build time.
Container filesystems are ephemeral by default
Changes made inside a container are lost when it stops. Use volumes for persistent data.
Default bridge network doesn't support automatic DNS
Containers on the default bridge must use IP addresses. Create a custom network for automatic DNS resolution by container name.
The container's main process runs as PID 1
PID 1 has special responsibilities (signal handling, reaping zombies). Use exec form CMD or an init system like tini.
Containers use copy-on-write for filesystem
Containers share read-only image layers. Writes go to a thin writable layer unique to each container.
Docker sends the entire build context to the daemon
Everything in your build directory (except .dockerignore patterns) is sent to Docker. Large contexts slow builds.
-p 8080:80 means host:container
The first port is on the host, the second is inside the container. -p 8080:80 forwards host port 8080 to container port 80.
ENV variables persist in the image and all derived containers
ENV sets environment variables that exist at runtime. ARG variables only exist during build.
Containers run as root by default
This is a security risk. Always add a USER instruction to run as non-root in production.
Interview & Exam Practice4
A colleague asks why you chose Docker containers over virtual machines for your microservices deployment.
What are the key differences and when would you choose each?
Your Docker build takes 10 minutes because it reinstalls npm dependencies every time, even when only application code changes.
How would you optimize the Dockerfile?
Your team deployed a PostgreSQL database in a Docker container. After a host reboot, all data was lost.
What went wrong and how should this be fixed?
A security audit flagged several issues with your Docker deployment: containers running as root, secrets in Dockerfiles, and unpatched base images.