π This space is for **beginners and new grads who want practical, no-nonsense tutorials. Explore more posts, project breakdowns, and my learning journey at kerempakten.dev!
π Table of Contents
- What is Docker?
- What Problems Does Docker Solve?
- Virtual Machines vs Docker
- Installing Docker
- Docker Images vs Containers
- Docker Registries
- Docker Image Versions (Tags)
- Essential Docker Commands
- Port Binding
- Starting and Stopping Containers
- Private Docker Registries
- Registry vs Repository
- Dockerfile - Dockerizing Your App
- Building Your Own Image
- Docker in the Software Development Lifecycle
- Where to Go From Here
What is Docker?
Docker is a containerization platform that allows you to package your application along with all its dependencies into a standardized unit called a container.
Think of it like this: Imagine you're moving to a new house. Instead of packing each item separately and hoping everything arrives intact, you put everything into a shipping container. That container can be moved anywhere in the world, and when you open it, everything is exactly as you packed it.
Docker does the same thing for software! π¦
Key Concept
A container is a lightweight, standalone, executable package that includes everything needed to run a piece of software: code, runtime, system tools, libraries, and settings.
What Problems Does Docker Solve?
The "It Works on My Machine" Problem π
If you've ever worked in a development team, you've probably heard (or said) this phrase. Here's a typical scenario:
Without Docker:
Developer A: "The app works perfectly on my laptop!"
Developer B: "It's crashing on mine..."
Developer A: "What version of Node are you running?"
Developer B: "Node? I thought this was a Python project..."
This happens because different machines have:
- Different operating systems
- Different versions of programming languages
- Different installed libraries
- Different environment configurations
How Docker Solves This
With Docker, you define your application's environment once in a configuration file. Everyone who runs your container gets the exact same environment.
Developer A: "Here's the Docker container"
Developer B: "It works perfectly!" β
Developer C: "It works perfectly!" β
Production Server: "It works perfectly!" β
The Deployment Nightmare
Traditional Deployment:
1. Set up server
2. Install correct OS version
3. Install correct Node.js version
4. Install correct MongoDB version
5. Install correct Redis version
6. Configure environment variables
7. Configure networking
8. Cross fingers and pray π€
9. Debug for 3 hours when something breaks
Docker Deployment:
1. Install Docker on server
2. Run: docker-compose up
3. Done β
Real-World Example
Let's say you're building a web application that needs:
- Node.js v18
- MongoDB 6.0
- Redis 7.0
- Specific npm packages
Without Docker:
- Each developer must install all these manually
- Version conflicts are common
- "But I have MongoDB 5.0 installed for another project!"
With Docker:
- Run one command, everything spins up
- Each project has its own isolated environment
- No conflicts between projects
Virtual Machines vs Docker
This is one of the most important concepts to understand!
Virtual Machines (VMs)
A VM is like having a separate computer inside your computer. It includes:
- Full operating system (Windows, Linux, etc.)
- Virtual hardware (CPU, RAM, disk)
- All system files
βββββββββββββββββββββββββββββββββββββββββββ
β Your Computer β
βββββββββββββββββββββββββββββββββββββββββββ€
β Host OS (Windows) β
βββββββββββββββββββββββββββββββββββββββββββ€
β Hypervisor β
βββββββββββββββββ¬ββββββββββββββββ¬ββββββββββ€
β VM 1 β VM 2 β VM 3 β
βββββββββββββββββΌββββββββββββββββΌββββββββββ€
β Guest OS β Guest OS β Guest OSβ
β (Ubuntu) β (CentOS) β (Debian)β
βββββββββββββββββΌββββββββββββββββΌββββββββββ€
β Your App β Your App β Your Appβ
βββββββββββββββββ΄ββββββββββββββββ΄ββββββββββ
Problem: Each VM needs its own OS, which means:
- Takes up GBs of disk space
- Uses significant RAM
- Slow to start (minutes)
- Resource heavy
Docker Containers
Containers share the host OS kernel and only include what's necessary for your app:
βββββββββββββββββββββββββββββββββββββββββββ
β Your Computer β
βββββββββββββββββββββββββββββββββββββββββββ€
β Host OS (Linux) β
βββββββββββββββββββββββββββββββββββββββββββ€
β Docker Engine β
βββββββββββββββββ¬ββββββββββββββββ¬ββββββββββ€
β Container 1 β Container 2 βContainerβ
βββββββββββββββββΌββββββββββββββββΌββββββββββ€
β Your App β Your App βYour App β
β + Deps only β + Deps only β+ Deps β
βββββββββββββββββ΄ββββββββββββββββ΄ββββββββββ
Comparison Table
| Feature | Virtual Machine | Docker Container |
|---|---|---|
| Size | GBs | MBs |
| Startup Time | Minutes | Seconds |
| Performance | Slower (full OS overhead) | Near-native |
| Isolation | Complete (separate OS) | Process-level |
| Resource Usage | High | Low |
| Portability | Limited | Excellent |
When to Use What?
Use VMs when:
- You need complete isolation
- You need to run different operating systems
- Security is critical (separate kernels)
Use Docker when:
- You need lightweight, fast deployments
- You're deploying microservices
- You want consistent environments
- You need to scale quickly
Installing Docker
Docker is available for all major operating systems.
For Mac
- Download Docker Desktop for Mac
- Open the
.dmgfile - Drag Docker to Applications
- Open Docker from Applications
- Verify installation:
docker --version
# Output: Docker version 24.x.x, build xxxxx
For Windows
- Download Docker Desktop for Windows
- Run the installer
- Enable WSL 2 (Windows Subsystem for Linux) if prompted
- Restart your computer
- Verify installation:
docker --version
For Linux (Ubuntu/Debian)
# Update package index
sudo apt-get update
# Install prerequisites
sudo apt-get install ca-certificates curl gnupg
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Set up the repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Verify installation
docker --version
Post-Installation (Linux)
To run Docker without sudo:
sudo groupadd docker
sudo usermod -aG docker $USER
# Log out and back in
Docker Images vs Containers
This is crucial to understand! Many beginners confuse these two concepts.
Docker Image π
An image is a read-only template containing:
- Application code
- Runtime environment
- Libraries and dependencies
- Environment variables
- Configuration files
Think of it as a recipe or a blueprint.
Docker Container π¦
A container is a running instance of an image. It's the actual application running in isolation.
Think of it as the actual dish made from the recipe.
Analogy: Class vs Object
If you're familiar with programming:
// Image = Class (the blueprint)
class Car {
constructor(color) {
this.color = color;
}
}
// Container = Object (the instance)
const myCar = new Car('red'); // Container 1
const yourCar = new Car('blue'); // Container 2
Visual Representation
βββββββββββββββββββββββββββββββββββββββββββ
β Docker Image β
β (node:18-alpine) β
β β
β - Node.js v18 β
β - npm β
β - Alpine Linux base β
β β
β READ-ONLY TEMPLATE β
βββββββββββββββββββββββββββββββββββββββββββ
β
β docker run
β
βββββββββββββ΄ββββββββββββ
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β Container 1 β β Container 2 β
β (Running) β β (Running) β
β β β β
β App Instance β β App Instance β
β Port 3000 β β Port 3001 β
βββββββββββββββββ βββββββββββββββββ
Key Points
- You can create many containers from one image
- Containers are isolated from each other
- Changes in a container don't affect the image
- When a container is deleted, its data is lost (unless using volumes)
Docker Registries
A Docker Registry is like GitHub but for Docker images. It's a storage and distribution system for Docker images.
Docker Hub
Docker Hub is the default public registry. It contains:
- Official Images: Maintained by Docker (nginx, node, postgres, etc.)
- Verified Publisher Images: From trusted companies (Microsoft, Oracle, etc.)
- Community Images: Created by anyone
How It Works
βββββββββββββββ docker push βββββββββββββββββββ
β Your β ββββββββββββββββββΊ β Docker Hub β
β Computer β β (Registry) β
β β ββββββββββββββββββ β β
βββββββββββββββ docker pull βββββββββββββββββββ
Finding Images
# Search for images
docker search nginx
# Or browse Docker Hub website
# https://hub.docker.com/
Popular Official Images
| Image | Description | Pull Command |
|---|---|---|
| nginx | Web server | docker pull nginx |
| node | Node.js runtime | docker pull node |
| postgres | PostgreSQL database | docker pull postgres |
| redis | In-memory data store | docker pull redis |
| mongo | MongoDB database | docker pull mongo |
| python | Python runtime | docker pull python |
Docker Image Versions (Tags)
Images come in different versions, managed through tags.
Understanding Tags
image_name:tag
Examples:
node:18 # Node.js version 18
node:18-alpine # Node.js 18 on Alpine Linux (smaller)
node:latest # Latest version (default)
node:lts # Long Term Support version
Why Tags Matter
# This might break your app in the future!
docker pull node:latest
# This is predictable and safe
docker pull node:18.17.0
Best Practices
β
DO:
# Use specific versions in production
docker pull node:18.17.0-alpine
docker pull postgres:15.3
β DON'T:
# Avoid 'latest' in production
docker pull node:latest # Could be v18 today, v20 tomorrow!
Common Tag Patterns
| Tag | Meaning |
|---|---|
18 |
Major version only |
18.17 |
Major + minor version |
18.17.0 |
Exact version (most precise) |
alpine |
Minimal Alpine Linux base (~5MB) |
slim |
Reduced Debian image |
bullseye |
Based on Debian Bullseye |
Essential Docker Commands
Let's learn the commands you'll use daily!
Pulling Images
# Pull an image from Docker Hub
docker pull nginx
# Pull specific version
docker pull nginx:1.25
# Pull from different registry
docker pull gcr.io/google-samples/hello-app:1.0
Running Containers
# Basic run (foreground)
docker run nginx
# Run in background not on terminal (-d = detached)
docker run -d nginx
# Run with a custom name
docker run -d --name my-nginx nginx
# Run and remove when stopped
docker run --rm nginx
Listing Containers & Images
# List running containers
docker ps
# List ALL containers (including stopped)
docker ps -a
# List images
docker images
# List images with details
docker images -a
Example Output
$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
a1b2c3d4e5f6 nginx "/docker-entrypoint.β¦" Up 2 minutes 80/tcp my-nginx
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a1b2c3d4e5f6 2 weeks ago 142MB
node 18 b2c3d4e5f6a7 3 weeks ago 991MB
Port Binding
By default, containers are isolated. To access a containerized app from your host machine, you need port binding.
The Problem
βββββββββββββββββββ βββββββββββββββββββ
β Your Browser β β β Container β
β localhost:80 β ββββΊ β nginx:80 β
βββββββββββββββββββ βββββββββββββββββββ
Can't connect! Container is isolated.
The Solution: Port Binding
# Syntax: -p HOST_PORT:CONTAINER_PORT
docker run -d -p 8080:80 nginx
βββββββββββββββββββ βββββββββββββββββββ
β Your Browser β β β Container β
β localhost:8080 β ββββΊ β nginx:80 β
βββββββββββββββββββ βββββββββββββββββββ
Port 8080 on host maps to port 80 in container
Multiple Port Bindings
# Bind multiple ports
docker run -d -p 8080:80 -p 8443:443 nginx
# Random host port
docker run -d -p 80 nginx # Docker assigns random host port
Real-World Example
# Run PostgreSQL with port binding
docker run -d \
--name my-postgres \
-p 5432:5432 \
-e POSTGRES_PASSWORD=mysecretpassword \
postgres:15
Now you can connect to PostgreSQL at localhost:5432!
Starting and Stopping Containers
Stop a Running Container
# Stop by name
docker stop my-nginx
# Stop by ID
docker stop a1b2c3d4e5f6
# Stop multiple containers
docker stop container1 container2 container3
Start a Stopped Container
# Start by name
docker start my-nginx
# Start by ID
docker start a1b2c3d4e5f6
Restart a Container
docker restart my-nginx
Remove a Container
# Remove stopped container
docker rm my-nginx
# Force remove running container
docker rm -f my-nginx
# Remove all stopped containers
docker container prune
View Container Logs
# View logs
docker logs my-nginx
# Follow logs (live)
docker logs -f my-nginx
# Last 100 lines
docker logs --tail 100 my-nginx
Execute Commands in Container
# Open interactive shell
docker exec -it my-nginx bash
# Run single command
docker exec my-nginx ls -la
# Check environment variables
docker exec my-nginx env
Private Docker Registries
While Docker Hub is public, companies often need private registries for proprietary code.
Popular Private Registry Options
| Provider | Description |
|---|---|
| Docker Hub Private | Paid private repos on Docker Hub |
| AWS ECR | Amazon Elastic Container Registry |
| Google GCR | Google Container Registry |
| Azure ACR | Azure Container Registry |
| GitHub GHCR | GitHub Container Registry |
| Self-hosted | Harbor, Nexus, GitLab Registry |
Using AWS ECR (Example)
# Login to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
# Pull from ECR
docker pull 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0
# Push to ECR
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0
Why Use Private Registries?
- Security: Keep proprietary code private
- Control: Manage access with IAM/RBAC
- Speed: Faster pulls within same cloud network
- Compliance: Meet regulatory requirements
Registry vs Repository
This is often confused! Let me clarify:
Registry
A registry is the server that stores Docker images.
Examples:
-
docker.io(Docker Hub) -
gcr.io(Google) -
123456789.dkr.ecr.us-east-1.amazonaws.com(AWS ECR)
Repository
A repository is a collection of related images with different tags.
Example:
-
nginxrepository contains:nginx:latestnginx:1.25nginx:1.25-alpinenginx:1.24
Full Image Reference
registry/repository:tag
Examples:
docker.io/library/nginx:1.25 # Full reference
nginx:1.25 # Short (defaults to docker.io/library)
gcr.io/my-project/my-app:v1.0 # Google Container Registry
Visual Representation
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Hub (Registry) β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ββββββββββββββββ ββββββββββββββββ β
β β nginx β β node β β
β β (Repository) β β (Repository) β ... β
β ββββββββββββββββ€ ββββββββββββββββ€ β
β β :latest β β :18 β β
β β :1.25 β β :18-alpine β β
β β :1.25-alpine β β :20 β β
β β :1.24 β β :latest β β
β ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Dockerfile - Dockerizing Your App
A Dockerfile is a text file containing instructions to build a Docker image. It's like a recipe for creating your container!
Basic Dockerfile Structure
# Base image
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy source code
COPY . .
# Expose port
EXPOSE 3000
# Start command
CMD ["npm", "start"]
Dockerfile Instructions Explained
| Instruction | Purpose | Example |
|---|---|---|
FROM |
Base image to build upon | FROM node:18-alpine |
WORKDIR |
Set working directory | WORKDIR /app |
COPY |
Copy files from host to image | COPY . . |
RUN |
Execute command during build | RUN npm install |
EXPOSE |
Document which port the app uses | EXPOSE 3000 |
CMD |
Default command when container starts | CMD ["npm", "start"] |
ENV |
Set environment variable | ENV NODE_ENV=production |
1. Use specific base image versions:
# β
Good
FROM node:18.17.0-alpine
# β Bad
FROM node:latest
2. Order instructions by change frequency:
# Things that change rarely (top)
FROM node:18-alpine
WORKDIR /app
# Dependencies (change occasionally)
COPY package*.json ./
RUN npm ci
# Source code (changes often - bottom)
COPY . .
3. Use .dockerignore:
# .dockerignore
node_modules
npm-debug.log
.git
.env
*.md
4. Minimize layers:
# β
Good - single RUN command
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
# β Bad - multiple layers
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
Building Your Own Image
Now let's build the image from our Dockerfile!
Build Command
# Basic build
docker build -t my-app .
# Build with specific tag
docker build -t my-app:v1.0 .
# Build with different Dockerfile
docker build -f Dockerfile.prod -t my-app:prod .
Build Process Explained
$ docker build -t my-app:v1.0 .
[+] Building 15.2s (10/10) FINISHED
=> [1/6] FROM node:18-alpine 2.1s
=> [2/6] WORKDIR /app 0.1s
=> [3/6] COPY package*.json ./ 0.1s
=> [4/6] RUN npm ci --only=production 10.2s
=> [5/6] COPY src/ ./src/ 0.1s
=> exporting to image 0.3s
Verify Your Image
# List images
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-app v1.0 abc123def456 10 seconds ago 175MB
Run Your Custom Image
# Run in detached mode with port binding
docker run -d -p 3000:3000 --name my-app-container my-app:v1.0
# Test it
curl http://localhost:3000
# Output: {"message":"Hello from Docker! π³","timestamp":"2024-01-15T10:30:00.000Z"}
Push to Registry
# Tag for Docker Hub
docker tag my-app:v1.0 yourusername/my-app:v1.0
# Login to Docker Hub
docker login
# Push
docker push yourusername/my-app:v1.0
Docker in the Software Development Lifecycle
Let's see how Docker fits into the complete development workflow!
Development Phase
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LOCAL DEVELOPMENT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Developer writes code β
β β β
β βΌ β
β docker-compose up (starts all services) β
β β β
β βΌ β
β βββββββββββ βββββββββββ βββββββββββ β
β β Node.js β β MongoDB β β Redis β β
β β App β β DB β β Cache β β
β βββββββββββ βββββββββββ βββββββββββ β
β β β
β βΌ β
β Test locally at localhost:3000 β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
CI/CD Pipeline
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CI/CD PIPELINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. Developer pushes code to GitHub β
β β β
β βΌ β
β 2. CI server (GitHub Actions) triggers β
β β β
β βΌ β
β 3. Run tests in Docker container β
β β β
β βΌ β
β 4. Build Docker image β
β β β
β βΌ β
β 5. Push to Container Registry (ECR/GCR) β
β β β
β βΌ β
β 6. Deploy to Kubernetes/ECS β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Production Environment
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PRODUCTION (Kubernetes) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββ βββββββββββ βββββββββββ β
β β Pod 1 β β Pod 2 β β Pod 3 β Auto- β
β β my-app β β my-app β β my-app β scaling β
β βββββββββββ βββββββββββ βββββββββββ β
β β β β β
β βββββββββββββΌββββββββββββ β
β β β
β Load Balancer β
β β β
β Users π β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Benefits in SDLC
| Phase | Docker Benefit |
|---|---|
| Development | Consistent environment, quick setup |
| Testing | Isolated test environments |
| CI/CD | Reproducible builds |
| Staging | Mirror production exactly |
| Production | Easy scaling, quick deployments |
Where to Go From Here
Congratulations on learning Docker basics! π Here's your learning roadmap:
Immediate Next Steps
-
Learn Docker Compose
- Multi-container applications
- Define services in YAML
- Example: App + Database + Cache
-
Understand Volumes
- Persist data across container restarts
- Share data between containers
-
Docker Networking
- Bridge networks
- Container communication
- DNS in Docker
-
Multi-stage Builds
- Smaller production images
- Separate build and runtime
-
Docker Security
- Non-root users
- Image scanning
- Secrets management
-
Container Orchestration
- Kubernetes (K8s)
- Docker Swarm
- Amazon ECS
-
CI/CD with Docker
- GitHub Actions
- GitLab CI
- Jenkins
Recommended Resources
| Resource | Type | Link |
|---|---|---|
| TechWorld with Nana | YouTube | Channel |
| Docker Official Docs | Documentation | docs.docker.com |
| Play with Docker | Interactive Lab | labs.play-with-docker.com |
| Docker Curriculum | Tutorial | docker-curriculum.com |
Quick Reference Card
Most Used Commands
# Images
docker pull <image> # Download image
docker images # List images
docker rmi <image> # Remove image
# Containers
docker run -d -p 8080:80 nginx # Run container
docker ps # List running
docker ps -a # List all
docker stop <container> # Stop
docker start <container> # Start
docker rm <container> # Remove
docker logs <container> # View logs
docker exec -it <container> bash # Shell access
# Build
docker build -t myapp:v1 . # Build image
docker push myapp:v1 # Push to registry
# Cleanup
docker system prune # Remove unused data
docker container prune # Remove stopped containers
docker image prune # Remove dangling images
Summary
Docker is an essential tool for modern software development. Here's what we covered:
β
What Docker is - A containerization platform
β
Problems it solves - Consistency across environments
β
VMs vs Containers - Lightweight alternative to VMs
β
Images vs Containers - Blueprint vs running instance
β
Docker Hub - Public registry for images
β
Essential commands - pull, run, stop, logs, exec
β
Port binding - Exposing container ports
β
Dockerfile - Recipe for building images
β
SDLC integration - Development to production workflow
π¬ Join the Journey!
Found this helpful? I'm building this blog for beginners and new grads like us to learn and grow together. No gatekeeping β just honest, practical knowledge from one learner to another.
π Subscribe at kerempakten.dev to get notified when I publish new tutorials!
You'll find:
- π³ DevOps & Cloud tutorials
- π» Project breakdowns & real code
- π― Career tips for new graduates
- π Honest "building in public" updates
Let's learn together!
Top comments (0)