DEV Community

Cover image for Docker for Beginners: A Complete Guide to Containerisation
Ege Pakten
Ege Pakten

Posted on

Docker for Beginners: A Complete Guide to Containerisation

πŸš€ 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

  1. What is Docker?
  2. What Problems Does Docker Solve?
  3. Virtual Machines vs Docker
  4. Installing Docker
  5. Docker Images vs Containers
  6. Docker Registries
  7. Docker Image Versions (Tags)
  8. Essential Docker Commands
  9. Port Binding
  10. Starting and Stopping Containers
  11. Private Docker Registries
  12. Registry vs Repository
  13. Dockerfile - Dockerizing Your App
  14. Building Your Own Image
  15. Docker in the Software Development Lifecycle
  16. 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..."
Enter fullscreen mode Exit fullscreen mode

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!" βœ…
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Docker Deployment:

1. Install Docker on server
2. Run: docker-compose up
3. Done βœ…
Enter fullscreen mode Exit fullscreen mode

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β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

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   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

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

  1. Download Docker Desktop for Mac
  2. Open the .dmg file
  3. Drag Docker to Applications
  4. Open Docker from Applications
  5. Verify installation:
docker --version
# Output: Docker version 24.x.x, build xxxxx
Enter fullscreen mode Exit fullscreen mode

For Windows

  1. Download Docker Desktop for Windows
  2. Run the installer
  3. Enable WSL 2 (Windows Subsystem for Linux) if prompted
  4. Restart your computer
  5. Verify installation:
docker --version
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Post-Installation (Linux)

To run Docker without sudo:

sudo groupadd docker
sudo usermod -aG docker $USER
# Log out and back in
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

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     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Finding Images

# Search for images
docker search nginx

# Or browse Docker Hub website
# https://hub.docker.com/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Best Practices

βœ… DO:

# Use specific versions in production
docker pull node:18.17.0-alpine
docker pull postgres:15.3
Enter fullscreen mode Exit fullscreen mode

❌ DON'T:

# Avoid 'latest' in production
docker pull node:latest  # Could be v18 today, v20 tomorrow!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

The Solution: Port Binding

# Syntax: -p HOST_PORT:CONTAINER_PORT
docker run -d -p 8080:80 nginx
Enter fullscreen mode Exit fullscreen mode
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Your Browser   β”‚  βœ“   β”‚   Container     β”‚
β”‚  localhost:8080 β”‚ ───► β”‚   nginx:80      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          Port 8080 on host maps to port 80 in container
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Real-World Example

# Run PostgreSQL with port binding
docker run -d \
  --name my-postgres \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=mysecretpassword \
  postgres:15
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Start a Stopped Container

# Start by name
docker start my-nginx

# Start by ID
docker start a1b2c3d4e5f6
Enter fullscreen mode Exit fullscreen mode

Restart a Container

docker restart my-nginx
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  • nginx repository contains:
    • nginx:latest
    • nginx:1.25
    • nginx:1.25-alpine
    • nginx: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
Enter fullscreen mode Exit fullscreen mode

Visual Representation

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Docker Hub (Registry)              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚
β”‚  β”‚    nginx     β”‚  β”‚     node     β”‚            β”‚
β”‚  β”‚ (Repository) β”‚  β”‚ (Repository) β”‚   ...      β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€            β”‚
β”‚  β”‚ :latest      β”‚  β”‚ :18          β”‚            β”‚
β”‚  β”‚ :1.25        β”‚  β”‚ :18-alpine   β”‚            β”‚
β”‚  β”‚ :1.25-alpine β”‚  β”‚ :20          β”‚            β”‚
β”‚  β”‚ :1.24        β”‚  β”‚ :latest      β”‚            β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 . .
Enter fullscreen mode Exit fullscreen mode

3. Use .dockerignore:

# .dockerignore
node_modules
npm-debug.log
.git
.env
*.md
Enter fullscreen mode Exit fullscreen mode

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/*
Enter fullscreen mode Exit fullscreen mode

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 .
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Verify Your Image

# List images
docker images

REPOSITORY   TAG     IMAGE ID       CREATED          SIZE
my-app       v1.0    abc123def456   10 seconds ago   175MB
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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                     β”‚
β”‚                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

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                        β”‚
β”‚                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Production Environment

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               PRODUCTION (Kubernetes)               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚ Pod 1   β”‚ β”‚ Pod 2   β”‚ β”‚ Pod 3   β”‚   Auto-      β”‚
β”‚  β”‚ my-app  β”‚ β”‚ my-app  β”‚ β”‚ my-app  β”‚   scaling    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚       β”‚           β”‚           β”‚                    β”‚
β”‚       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β”‚                   β”‚                                 β”‚
β”‚            Load Balancer                            β”‚
β”‚                   β”‚                                 β”‚
β”‚              Users 🌐                               β”‚
β”‚                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

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

  1. Learn Docker Compose

    • Multi-container applications
    • Define services in YAML
    • Example: App + Database + Cache
  2. Understand Volumes

    • Persist data across container restarts
    • Share data between containers
  3. Docker Networking

    • Bridge networks
    • Container communication
    • DNS in Docker
  4. Multi-stage Builds

    • Smaller production images
    • Separate build and runtime
  5. Docker Security

    • Non-root users
    • Image scanning
    • Secrets management
  6. Container Orchestration

    • Kubernetes (K8s)
    • Docker Swarm
    • Amazon ECS
  7. 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
Enter fullscreen mode Exit fullscreen mode

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)