Back to blog

Docker Fundamentals: Complete Beginner's Guide

dockercontainersdevopsbackendtutorial
Docker Fundamentals: Complete Beginner's Guide

Welcome to Phase 1 of the Docker & Kubernetes Learning Roadmap! In this comprehensive guide, you'll master Docker fundamentals—the foundation for all container-based development and deployment.

By the end of this post, you'll understand how Docker works, be able to run and manage containers, and have hands-on experience with real-world applications like Nginx, PostgreSQL, and Redis.

What You'll Learn

✅ Understand containers vs virtual machines
✅ Install Docker on any platform
✅ Work with Docker images and layers
✅ Run, stop, and manage containers
✅ Map ports and expose services
✅ Persist data with volumes
✅ Master essential Docker commands


What is Docker?

Docker is a platform that packages applications and their dependencies into containers—lightweight, portable, and isolated environments that run consistently across any system.

The Problem Docker Solves

Before containers:

Developer: "It works on my machine!"
Ops: "Well, it doesn't work on the server."

The root cause? Environmental differences:

  • Different OS versions
  • Different library versions
  • Different configurations
  • Missing dependencies

Docker solves this by packaging everything your app needs into a container:

┌─────────────────────────────────┐
│          Your Application        │
├─────────────────────────────────┤
│    Dependencies & Libraries      │
├─────────────────────────────────┤
│    Runtime (Node, Python, etc)   │
├─────────────────────────────────┤
│      Configuration Files         │
└─────────────────────────────────┘
         = Docker Container

Now, the same container runs identically on:

  • Your laptop
  • Your colleague's machine
  • CI/CD pipelines
  • Staging servers
  • Production environments

Containers vs Virtual Machines

Understanding the difference between containers and VMs is fundamental:

Virtual Machines

┌─────────────────────────────────────────────┐
│                 Your Apps                    │
├──────────────┬──────────────┬───────────────┤
│    App 1     │    App 2     │    App 3      │
├──────────────┼──────────────┼───────────────┤
│   Guest OS   │   Guest OS   │   Guest OS    │
├──────────────┴──────────────┴───────────────┤
│              Hypervisor (VMware, VirtualBox) │
├─────────────────────────────────────────────┤
│                  Host OS                     │
├─────────────────────────────────────────────┤
│                 Hardware                     │
└─────────────────────────────────────────────┘

Each VM includes:

  • Full guest operating system (several GB)
  • Virtualized hardware
  • Boot time: Minutes

Containers

┌─────────────────────────────────────────────┐
│                 Your Apps                    │
├──────────────┬──────────────┬───────────────┤
│ Container 1  │ Container 2  │ Container 3   │
├──────────────┴──────────────┴───────────────┤
│              Docker Engine                   │
├─────────────────────────────────────────────┤
│                  Host OS                     │
├─────────────────────────────────────────────┤
│                 Hardware                     │
└─────────────────────────────────────────────┘

Containers share the host OS kernel:

  • No guest OS (just app and dependencies)
  • Lightweight (MBs instead of GBs)
  • Start time: Seconds (or less)

Comparison Table

FeatureVirtual MachineContainer
SizeGBs (full OS)MBs (app + deps)
StartupMinutesSeconds
IsolationComplete (hardware-level)Process-level
Performance~5-10% overheadNear-native
Density~10s per host~100s per host
PortabilityMediumHigh

When to Use Each

Use VMs when:

  • You need complete isolation (security)
  • Running different operating systems
  • Legacy applications requiring specific OS

Use Containers when:

  • Microservices architecture
  • CI/CD pipelines
  • Development environments
  • Cloud-native applications
  • Maximum resource efficiency

Docker Architecture

Docker uses a client-server architecture:

┌─────────────────────────────────────────────────────────────┐
│                        Docker Host                           │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                   Docker Daemon                       │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │ Container 1 │  │ Container 2 │  │ Container 3 │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  │                                                       │   │
│  │  ┌─────────────┐  ┌─────────────┐                   │   │
│  │  │   Image A   │  │   Image B   │                   │   │
│  │  └─────────────┘  └─────────────┘                   │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
           ▲                                    │
           │ Docker CLI                         │ Pull/Push
           │                                    ▼
┌──────────────────┐                  ┌──────────────────┐
│   Docker Client  │                  │  Docker Registry │
│   (docker CLI)   │                  │  (Docker Hub)    │
└──────────────────┘                  └──────────────────┘

Key Components

1. Docker Client (docker)

  • Command-line interface
  • Sends commands to Docker daemon
  • Can connect to remote Docker hosts

2. Docker Daemon (dockerd)

  • Background service running on host
  • Manages containers, images, networks, volumes
  • Listens for Docker API requests

3. Docker Registry (Docker Hub)

  • Stores Docker images
  • Docker Hub is the default public registry
  • Private registries available (ECR, GCR, ACR)

4. Container Runtime

  • containerd: Industry-standard container runtime
  • runc: Low-level runtime that creates containers

Docker Desktop vs Docker Engine

Docker DesktopDocker Engine
PlatformmacOS, WindowsLinux
IncludesGUI, Docker Engine, KubernetesCLI only
Use CaseDevelopmentDevelopment + Production
LicenseFree for personal, paid for enterpriseFree

Installing Docker

macOS and Windows: Docker Desktop

  1. Download Docker Desktop from docker.com/products/docker-desktop
  2. Run the installer
  3. Start Docker Desktop
  4. Verify installation:
docker --version
# Docker version 24.0.7, build afdd53b
 
docker run hello-world
# Hello from Docker!
# This message shows that your installation appears to be working correctly.

Linux: Docker Engine

Ubuntu/Debian:

# Update package index
sudo apt-get update
 
# Install prerequisites
sudo apt-get install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
 
# Add Docker's official GPG key
sudo mkdir -p /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 \
  $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
 
# Install Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
 
# Verify installation
sudo docker run hello-world

Post-installation (Linux):

# Add your user to the docker group (no sudo needed)
sudo usermod -aG docker $USER
 
# Log out and back in, then verify
docker run hello-world

Docker Images

An image is a read-only template containing:

  • Application code
  • Runtime (Node.js, Python, Java, etc.)
  • Libraries and dependencies
  • Configuration files

Think of an image as a snapshot or blueprint for creating containers.

Image Layers

Docker images are built in layers:

┌─────────────────────────────────┐
│     Your Application Code       │  ← Layer 4 (your code)
├─────────────────────────────────┤
│     npm install / pip install   │  ← Layer 3 (dependencies)
├─────────────────────────────────┤
│     Node.js / Python Runtime    │  ← Layer 2 (runtime)
├─────────────────────────────────┤
│     Ubuntu / Alpine Linux       │  ← Layer 1 (base OS)
└─────────────────────────────────┘

Why layers matter:

  • Caching: Unchanged layers are reused
  • Sharing: Multiple images can share base layers
  • Efficiency: Only changed layers are downloaded/uploaded

Pulling Images

Download images from Docker Hub:

# Pull latest tag (default)
docker pull nginx
 
# Pull specific version
docker pull nginx:1.25
 
# Pull specific architecture
docker pull --platform linux/amd64 nginx:1.25
 
# Pull from different registry
docker pull ghcr.io/owner/image:tag

Image Naming Convention

registry/repository:tag
 
Examples:
docker.io/library/nginx:1.25     # Full official image path
nginx:1.25                        # Short form (defaults to docker.io/library/)
gcr.io/my-project/my-app:v1.0    # Google Container Registry
myregistry.com/team/app:latest   # Private registry

Common tags:

  • latest - Most recent version (avoid in production!)
  • 1.25, 1.25.3 - Specific versions
  • alpine - Alpine Linux-based (smaller)
  • slim - Debian slim (smaller)

Listing and Inspecting Images

# List all local images
docker images
# REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
# nginx        1.25      a8758716bb6a   2 weeks ago   187MB
# postgres     15        ceccf204404e   3 weeks ago   379MB
 
# Show image details
docker image inspect nginx:1.25
 
# Show image history (layers)
docker image history nginx:1.25
 
# Remove an image
docker rmi nginx:1.25
 
# Remove unused images
docker image prune

Official vs Community Images

Official Images (verified by Docker):

  • No prefix: nginx, postgres, node, python
  • Maintained by Docker or software vendors
  • Regularly updated and scanned for vulnerabilities

Community Images:

  • Username prefix: bitnami/nginx, linuxserver/nginx
  • Quality varies
  • Check stars, downloads, and last update

Docker Containers

A container is a running instance of an image. You can create multiple containers from the same image.

Image (blueprint)  →  Container (running instance)
      nginx:1.25   →  my-nginx (running)
                   →  my-nginx-2 (running)
                   →  my-nginx-3 (stopped)

Running Containers

Basic run:

# Run a container (pulls image if not local)
docker run nginx
 
# Run in detached mode (background)
docker run -d nginx
 
# Run with a name
docker run -d --name my-nginx nginx
 
# Run and remove when stopped
docker run --rm nginx

Interactive mode:

# Run with interactive terminal
docker run -it ubuntu bash
 
# Inside the container
root@abc123:/# ls
root@abc123:/# exit

Container Lifecycle

                  create
    Image ─────────────────► Container (Created)

                                   │ start

                              Container (Running)

                    ┌──────────────┼──────────────┐
                    │              │              │
                 stop           pause          kill
                    │              │              │
                    ▼              ▼              ▼
              Container       Container      Container
              (Stopped)       (Paused)       (Stopped)
                    │              │
                    │           unpause
                    │              │
                    └──────────────┼──────────────┐
                                   │              │
                               restart         remove
                                   │              │
                                   ▼              ▼
                              Container       (Deleted)
                              (Running)

Lifecycle commands:

# Create without starting
docker create --name my-nginx nginx
 
# Start a created/stopped container
docker start my-nginx
 
# Stop a running container (graceful)
docker stop my-nginx
 
# Kill a container (immediate)
docker kill my-nginx
 
# Restart a container
docker restart my-nginx
 
# Pause a container (freeze processes)
docker pause my-nginx
 
# Unpause a container
docker unpause my-nginx
 
# Remove a stopped container
docker rm my-nginx
 
# Force remove a running container
docker rm -f my-nginx

Listing Containers

# List running containers
docker ps
 
# List all containers (including stopped)
docker ps -a
 
# List with custom format
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
 
# List only container IDs
docker ps -q

Executing Commands in Containers

# Run a command in a running container
docker exec my-nginx ls /etc/nginx
 
# Open an interactive shell
docker exec -it my-nginx bash
 
# Run as a specific user
docker exec -u root my-nginx whoami
 
# Set environment variable for command
docker exec -e MY_VAR=value my-nginx env

Viewing Logs

# View all logs
docker logs my-nginx
 
# Follow logs (like tail -f)
docker logs -f my-nginx
 
# Show last 100 lines
docker logs --tail 100 my-nginx
 
# Show logs since timestamp
docker logs --since 2024-01-01T00:00:00 my-nginx
 
# Show logs with timestamps
docker logs -t my-nginx

Container Stats and Info

# View resource usage (CPU, memory)
docker stats
 
# View specific container stats
docker stats my-nginx
 
# Inspect container details
docker inspect my-nginx
 
# View container processes
docker top my-nginx

Port Mapping

By default, containers are isolated from the host network. Port mapping exposes container ports to the host.

Syntax

docker run -p HOST_PORT:CONTAINER_PORT image
 
# Examples:
docker run -p 8080:80 nginx      # Host 8080 → Container 80
docker run -p 3000:3000 node     # Same port
docker run -p 127.0.0.1:8080:80  # Only localhost

Practical Examples

Web server on port 8080:

# Run Nginx on host port 8080
docker run -d --name web -p 8080:80 nginx
 
# Access in browser: http://localhost:8080
curl http://localhost:8080

Multiple port mappings:

# Map multiple ports
docker run -d --name app \
    -p 80:80 \
    -p 443:443 \
    nginx

Publish all exposed ports:

# Let Docker choose host ports
docker run -d -P nginx
 
# Check assigned ports
docker port container_name
# 80/tcp -> 0.0.0.0:32768

View Port Mappings

# Show ports for a container
docker port my-nginx
# 80/tcp -> 0.0.0.0:8080
 
# Show in ps output
docker ps
# PORTS
# 0.0.0.0:8080->80/tcp

Data Persistence with Volumes

Containers are ephemeral—when removed, all data inside is lost. Volumes provide persistent storage.

Three Types of Mounts

┌────────────────────────────────────────────────────────────┐
│                        Docker Host                          │
│                                                              │
│  ┌────────────────┐    ┌────────────────────────────────┐  │
│  │   Bind Mount   │    │         Named Volume           │  │
│  │ /home/user/app │    │    /var/lib/docker/volumes/    │  │
│  └───────┬────────┘    └───────────────┬────────────────┘  │
│          │                             │                    │
│          │     ┌───────────────────┐   │                    │
│          │     │    tmpfs mount    │   │                    │
│          │     │   (memory only)   │   │                    │
│          │     └─────────┬─────────┘   │                    │
│          │               │             │                    │
│          ▼               ▼             ▼                    │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                     Container                         │  │
│  │   /app          /tmp/cache         /data              │  │
│  └──────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────┘

Docker manages the storage location:

# Create a volume
docker volume create my-data
 
# List volumes
docker volume ls
 
# Use volume in container
docker run -d \
    --name postgres \
    -v my-data:/var/lib/postgresql/data \
    postgres:15
 
# Inspect volume
docker volume inspect my-data
 
# Remove volume
docker volume rm my-data
 
# Remove unused volumes
docker volume prune

Why use named volumes?

  • Docker manages location
  • Works across platforms
  • Easy backup and migration
  • Better for production

2. Bind Mounts

Mount a host directory into the container:

# Mount current directory
docker run -d \
    --name dev-app \
    -v $(pwd):/app \
    node:20
 
# Mount with read-only
docker run -d \
    -v $(pwd)/config:/etc/app/config:ro \
    my-app
 
# Absolute path
docker run -d \
    -v /home/user/data:/data \
    my-app

Use cases for bind mounts:

  • Development (hot reload)
  • Configuration files
  • Sharing files between host and container

3. tmpfs Mounts

Store data in memory (never written to disk):

docker run -d \
    --name secure-app \
    --tmpfs /tmp:size=100m \
    my-app

Use cases:

  • Sensitive data (secrets)
  • Temporary caches
  • Performance-critical temporary storage

Practical Volume Examples

PostgreSQL with persistent data:

# Create volume for database
docker volume create postgres-data
 
# Run PostgreSQL
docker run -d \
    --name postgres \
    -e POSTGRES_PASSWORD=secret \
    -v postgres-data:/var/lib/postgresql/data \
    -p 5432:5432 \
    postgres:15
 
# Data persists even after container removal
docker rm -f postgres
docker run -d \
    --name postgres-new \
    -e POSTGRES_PASSWORD=secret \
    -v postgres-data:/var/lib/postgresql/data \
    -p 5432:5432 \
    postgres:15
# Your data is still there!

Development with hot reload:

# Mount source code for development
docker run -d \
    --name dev \
    -v $(pwd)/src:/app/src \
    -p 3000:3000 \
    node:20 npm run dev

Environment Variables

Configure containers at runtime with environment variables.

Passing Environment Variables

# Single variable
docker run -e DATABASE_URL=postgres://localhost/db my-app
 
# Multiple variables
docker run \
    -e DATABASE_URL=postgres://localhost/db \
    -e REDIS_URL=redis://localhost:6379 \
    -e NODE_ENV=production \
    my-app
 
# From host environment
export API_KEY=secret123
docker run -e API_KEY my-app
 
# From file
docker run --env-file .env my-app

.env file format:

# .env
DATABASE_URL=postgres://localhost:5432/mydb
REDIS_URL=redis://localhost:6379
NODE_ENV=production
API_KEY=secret123

Practical Examples

PostgreSQL with environment:

docker run -d \
    --name postgres \
    -e POSTGRES_USER=myuser \
    -e POSTGRES_PASSWORD=mypassword \
    -e POSTGRES_DB=myapp \
    -p 5432:5432 \
    postgres:15

Redis with password:

docker run -d \
    --name redis \
    -e REDIS_PASSWORD=secret \
    -p 6379:6379 \
    redis:7 \
    redis-server --requirepass secret

Node.js application:

docker run -d \
    --name api \
    -e NODE_ENV=production \
    -e PORT=3000 \
    -e DATABASE_URL=postgres://postgres:secret@db:5432/app \
    --env-file .env.production \
    -p 3000:3000 \
    my-node-app

View Container Environment

# See all environment variables
docker exec my-app env
 
# Inspect container for env vars
docker inspect my-app --format='{{range .Config.Env}}{{println .}}{{end}}'

Essential Docker Commands Reference

Here's a complete reference of commands you'll use daily:

Images

# Pull an image
docker pull nginx:1.25
 
# List images
docker images
 
# Remove image
docker rmi nginx:1.25
 
# Remove unused images
docker image prune
 
# Remove all unused images
docker image prune -a
 
# Build image (requires Dockerfile)
docker build -t my-app:1.0 .
 
# Tag image
docker tag my-app:1.0 myregistry.com/my-app:1.0
 
# Push image to registry
docker push myregistry.com/my-app:1.0
 
# Save image to file
docker save -o my-app.tar my-app:1.0
 
# Load image from file
docker load -i my-app.tar

Containers

# Run container
docker run -d --name my-app -p 8080:80 nginx
 
# List running containers
docker ps
 
# List all containers
docker ps -a
 
# Stop container
docker stop my-app
 
# Start container
docker start my-app
 
# Restart container
docker restart my-app
 
# Remove container
docker rm my-app
 
# Remove running container
docker rm -f my-app
 
# Remove all stopped containers
docker container prune
 
# View logs
docker logs my-app
docker logs -f my-app  # Follow
 
# Execute command
docker exec -it my-app bash
 
# Copy files
docker cp my-app:/etc/nginx/nginx.conf ./nginx.conf
docker cp ./nginx.conf my-app:/etc/nginx/nginx.conf
 
# View resource usage
docker stats

Volumes

# Create volume
docker volume create my-data
 
# List volumes
docker volume ls
 
# Inspect volume
docker volume inspect my-data
 
# Remove volume
docker volume rm my-data
 
# Remove unused volumes
docker volume prune

Networks

# List networks
docker network ls
 
# Create network
docker network create my-network
 
# Connect container to network
docker network connect my-network my-app
 
# Disconnect from network
docker network disconnect my-network my-app
 
# Remove network
docker network rm my-network

System

# View Docker system info
docker info
 
# View disk usage
docker system df
 
# Clean up everything unused
docker system prune
 
# Clean up with volumes
docker system prune --volumes
 
# View Docker version
docker version

Hands-On Examples

Let's practice with real applications:

Example 1: Running Nginx Web Server

# Run Nginx
docker run -d \
    --name web \
    -p 8080:80 \
    nginx:1.25
 
# Verify it's running
curl http://localhost:8080
 
# View logs
docker logs web
 
# Check inside the container
docker exec -it web bash
cat /etc/nginx/nginx.conf
exit
 
# Stop and remove
docker stop web
docker rm web

Example 2: PostgreSQL Database

# Create volume for data
docker volume create pgdata
 
# Run PostgreSQL
docker run -d \
    --name postgres \
    -e POSTGRES_USER=admin \
    -e POSTGRES_PASSWORD=secret \
    -e POSTGRES_DB=myapp \
    -v pgdata:/var/lib/postgresql/data \
    -p 5432:5432 \
    postgres:15
 
# Connect to database
docker exec -it postgres psql -U admin -d myapp
 
# Inside psql
CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(100));
INSERT INTO users (name) VALUES ('Alice'), ('Bob');
SELECT * FROM users;
\q
 
# Stop, remove, and restart - data persists!
docker stop postgres
docker rm postgres
docker run -d \
    --name postgres \
    -e POSTGRES_USER=admin \
    -e POSTGRES_PASSWORD=secret \
    -e POSTGRES_DB=myapp \
    -v pgdata:/var/lib/postgresql/data \
    -p 5432:5432 \
    postgres:15
 
# Verify data still exists
docker exec -it postgres psql -U admin -d myapp -c "SELECT * FROM users;"

Example 3: Redis Cache

# Run Redis
docker run -d \
    --name redis \
    -p 6379:6379 \
    redis:7
 
# Connect with redis-cli
docker exec -it redis redis-cli
 
# Inside redis-cli
SET greeting "Hello, Docker!"
GET greeting
exit
 
# With persistence
docker run -d \
    --name redis-persistent \
    -v redis-data:/data \
    -p 6379:6379 \
    redis:7 \
    redis-server --appendonly yes

Example 4: Development Environment

# Create a project directory
mkdir my-node-app && cd my-node-app
 
# Create a simple app
cat > index.js << 'EOF'
const http = require('http');
 
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello from Docker!\n');
});
 
server.listen(3000, () => {
  console.log('Server running on port 3000');
});
EOF
 
# Run with mounted source code
docker run -d \
    --name dev \
    -v $(pwd):/app \
    -w /app \
    -p 3000:3000 \
    node:20 \
    node index.js
 
# Test
curl http://localhost:3000
 
# Modify index.js and restart
docker restart dev

Common Mistakes to Avoid

1. Running as Root

# Bad - running as root
docker run nginx
 
# Better - use non-root user if image supports it
docker run --user 1000:1000 nginx

2. Using :latest Tag

# Bad - unpredictable
docker run nginx:latest
 
# Good - specific version
docker run nginx:1.25.3

3. Storing Data in Containers

# Bad - data lost when container removed
docker run postgres:15
 
# Good - use volumes
docker run -v pgdata:/var/lib/postgresql/data postgres:15

4. Hardcoding Secrets

# Bad - secrets visible in docker inspect
docker run -e DATABASE_PASSWORD=secret123 my-app
 
# Better - use env file
docker run --env-file .env my-app
 
# Best - use Docker secrets (Swarm) or external secret manager

5. Not Cleaning Up

# Unused containers, images, and volumes accumulate
# Run periodically:
docker system prune --volumes

Troubleshooting Guide

Container Won't Start

# Check logs
docker logs container_name
 
# Check container status
docker inspect container_name --format='{{.State.Status}}'
 
# Check exit code
docker inspect container_name --format='{{.State.ExitCode}}'

Port Already in Use

# Error: port is already allocated
 
# Find what's using the port
lsof -i :8080
 
# Use a different port
docker run -p 8081:80 nginx

Permission Denied

# Error: permission denied
 
# Check if Docker daemon is running
sudo systemctl status docker
 
# Add user to docker group (Linux)
sudo usermod -aG docker $USER
# Log out and back in

Out of Disk Space

# Check Docker disk usage
docker system df
 
# Clean up
docker system prune --all --volumes

Best Practices Summary

Images

✅ Use specific version tags (not :latest)
✅ Prefer official images
✅ Use smaller base images (alpine, slim) when possible
✅ Regularly update images for security patches

Containers

✅ Run containers in detached mode (-d) for services
✅ Always name your containers (--name)
✅ Use --rm for one-off commands
✅ Set resource limits in production

Data

✅ Use named volumes for persistent data
✅ Use bind mounts only for development
✅ Never store data in container's writable layer
✅ Backup volumes regularly

Security

✅ Don't run as root when possible
✅ Use --env-file instead of -e for secrets
✅ Scan images for vulnerabilities
✅ Keep Docker updated


Practice Exercises

Exercise 1: Web Server

Deploy a custom Nginx configuration:

  1. Create a custom nginx.conf
  2. Run Nginx with your config mounted
  3. Verify your changes work

Exercise 2: Database Setup

Set up PostgreSQL with:

  1. Custom database name
  2. Persistent storage
  3. Initial data loaded from SQL file

Exercise 3: Multi-Container Communication

Run Redis and access it from another container:

  1. Create a Docker network
  2. Run Redis in that network
  3. Run another container and connect to Redis

What's Next?

You've mastered Docker fundamentals! In the next post, we'll cover:

  • Dockerfile creation for custom images
  • Docker Compose for multi-container applications
  • Multi-stage builds for optimized images
  • Development workflows with hot reload

Summary and Key Takeaways

✅ Docker packages applications into portable containers
✅ Containers are lightweight compared to VMs (MB vs GB)
✅ Docker uses client-server architecture (CLI → Daemon)
✅ Images are read-only templates; containers are running instances
✅ Port mapping exposes containers to the host network
✅ Volumes persist data beyond container lifecycle
✅ Environment variables configure containers at runtime
✅ Use specific image tags, named volumes, and proper cleanup


Series: Docker & Kubernetes Learning Roadmap
Next: Phase 2: Docker Compose & Multi-Container Apps (Coming Soon)


Have questions about Docker fundamentals? Feel free to reach out or leave a comment!

📬 Subscribe to Newsletter

Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.

We respect your privacy. Unsubscribe at any time.

💬 Comments

Sign in to leave a comment

We'll never post without your permission.