0% found this document useful (0 votes)
2 views92 pages

Docker Comprehensive Guide

The document is a comprehensive guide to Docker, covering its introduction, evolution, core concepts, installation procedures across different operating systems, and common CLI commands. It explains how Docker simplifies application deployment through containerization, allowing for efficient resource use and consistent environments. Additionally, it outlines the Docker ecosystem, real-world use cases, and post-installation security recommendations.

Uploaded by

Vinisha Gomare
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views92 pages

Docker Comprehensive Guide

The document is a comprehensive guide to Docker, covering its introduction, evolution, core concepts, installation procedures across different operating systems, and common CLI commands. It explains how Docker simplifies application deployment through containerization, allowing for efficient resource use and consistent environments. Additionally, it outlines the Docker ecosystem, real-world use cases, and post-installation security recommendations.

Uploaded by

Vinisha Gomare
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

DOCKER COMPREHENSIVE GUIDE

Module 1: Introduction to Docker

1.1 What is Docker?

Docker is an open-source platform designed to simplify building, shipping, and running applications
by using containerization. In essence, Docker allows developers to package applications with all the
necessary dependencies (binaries, libraries, configurations) into a single container image that can run
reliably across different computing environments.

A Docker container is a lightweight, stand-alone, executable package of software that includes


everything needed to run an application. Containers share the same OS kernel, which makes them
much faster, smaller, and more efficient than virtual machines.

1.2 The Evolution: From Bare Metal to Containers

Before we appreciate Docker, we must understand where it fits in the evolution of software
deployment:

Bare Metal Era

• Applications ran directly on physical machines.

• One app per server → Massive underutilization of resources.

• Scaling meant buying more servers (CAPEX-intensive).

• No isolation → Conflicts between software versions.

Virtual Machine Era

• Introduced hardware-level virtualization via hypervisors (e.g., VMware, VirtualBox, KVM).

• Allowed multiple OS instances (VMs) on a single physical host.

• Still heavyweight: Each VM carries its own full-blown OS.

• Slow boot times, large images, high resource consumption.

Container Era (Docker Revolution)

• Containers provide OS-level virtualization.

• Share the host’s kernel while isolating the app in a user space.

• Super lightweight → Instant startup, minimal overhead.

• Repeatable builds, consistent environments, easier CI/CD.


1.3 Virtual Machines vs Docker Containers

Feature Virtual Machines Docker Containers

Boot Time Minutes Seconds or less

OS Full OS per VM Shared host OS kernel

Resource Usage High (RAM, CPU, Disk) Low

Image Size GBs MBs (multi-stage builds)

Portability Low High

Performance Hypervisor overhead Near-native performance

Use Case Isolation, Legacy apps Microservices, DevOps, CI/CD

1.4 Real-World Analogy: Shipping Containers

Imagine the logistics of transporting goods across the world.

• Before standardized shipping containers, transporting goods required handling individual


items, repackaging, and often led to damage, delays, and confusion.

• After containers were standardized, logistics became modular. Goods could be transported
by truck, ship, rail – regardless of what's inside – using a consistent packaging method.

Docker does this for software:

• Your code (goods) is packaged in a container image (shipping container).

• It can run on any Docker-enabled host (truck, ship, train).

• No need to worry about what's inside – it just works.

1.5 Core Concepts and Terminologies

Docker Image

• A read-only template with the application and all its dependencies.

• Built using a Dockerfile.

• Multiple layers (cached, optimized).

• Can be versioned and shared (e.g., via Docker Hub).


Docker Container

• A runtime instance of an image.

• Lightweight and isolated.

• Ephemeral by default (unless data is persisted via volumes).

• Executes the app defined in the image.

Dockerfile

• A script containing instructions to build an image.

• Line-by-line instructions: FROM, COPY, RUN, CMD, EXPOSE, ENV, etc.

Docker Engine

• The core runtime that executes containers.

• Manages lifecycle: build, run, stop, remove containers.

• Consists of:

o Docker Daemon (dockerd): Background service.

o Docker CLI: Command-line interface.

o REST API: For third-party tools or programmatic control.

Docker Hub

• Docker’s official public registry.

• Store, share, and distribute Docker images.

• Private repos and automated builds supported.

1.6 Docker Architecture (Visualized)


+----------------------------+
| Docker CLI |
+-------------+-------------+
|
v
+-----------------------------+
| Docker REST API |
+-------------+--------------+
|
v
+-----------------------------------------+
| Docker Daemon |
| - Build Images |
| - Run Containers |
| - Manage Volumes, Networks |
+-------------+--------------------------+
|
v
+---------------------------------+
| Container Runtime |
| (containerd / runc) |
+---------------------------------+
Key Points:

• The CLI talks to the daemon via REST API.

• The daemon delegates to lower-level runtimes like containerd and runc.

• All of this is transparent to the user, who only interacts with the CLI or Docker Desktop GUI.

1.7 The Rise of Docker in DevOps & Cloud-Native

Docker Changed the Game by Enabling:

1. Consistency Across Environments

o No more “It works on my machine.”

o Same container runs in dev, staging, and prod.

2. Rapid CI/CD Pipelines

o Spin up throwaway environments for each commit.

o Run tests in isolated containers.

3. Microservices Adoption

o Each service in its own container.

o Easy scaling, independence, fault isolation.

4. Cloud Portability

o Container images can run on AWS, GCP, Azure, on-prem.

5. Infrastructure Simplification

o Reduced reliance on complex VM provisioning.

1.8 Docker Ecosystem

Docker is not just about containers. It’s a full ecosystem:

Component Description

Docker Engine Core service that runs and manages containers

Docker CLI Tool for interacting with the engine

Docker Compose Tool for managing multi-container applications

Docker Swarm Native clustering and orchestration

Docker Hub Registry to host and distribute container images


Component Description

Docker Desktop GUI + local environment for Windows/macOS

Docker Registry Private image registry (can be self-hosted)

BuildKit Faster, advanced image builder with better caching

Docker Scout Image analysis and security scanning

Docker Extensions Plugin ecosystem for UI and developer tools

1.9 Real-World Docker Use Cases

1. Application Containerization

• Monolith or microservice – isolate and run securely in containers.

• Common stacks: [Link] + MongoDB, Django + PostgreSQL, Go APIs.

2. Automated Testing

• CI jobs can spin up containerized environments.

• Tools like Cypress, Selenium, JMeter run tests in containers.

3. Cloud-Native Development

• Docker + Kubernetes = cornerstone of cloud-native apps.

• Rapid prototyping and deployment.

4. Legacy Application Modernization

• Containerize old apps for portability without rewriting.

5. Scaling and Horizontal Expansion

• Run multiple instances of the same container behind a load balancer.


Module 2: Installing Docker

2.1 Overview: What Does Docker Installation Actually Mean?

When we say “install Docker,” we’re talking about setting up the entire runtime ecosystem that
allows you to:

• Build Docker images

• Run Docker containers

• Manage volumes, networks, registries

• Interact via CLI or GUI (like Docker Desktop)

Installing Docker Means Installing:

Component Purpose

dockerd (Docker Daemon) Runs in the background, manages images/containers

docker (CLI) User interface to interact with the daemon

containerd Lightweight runtime used internally by Docker

runc OCI-compliant runtime to run containers

docker-compose For managing multi-container apps

buildkit Optimized image build system

Different operating systems (Linux, Windows, macOS) come with different installation approaches.
Let’s go platform-by-platform.

2.2 Docker Installation on Linux (Ubuntu/Debian)

Step-by-Step Guide (Official Docker CE)

# Step 1: Remove old Docker versions (if any)


sudo apt remove docker docker-engine [Link] containerd runc

# Step 2: Update package index


sudo apt update

# Step 3: Install required packages for HTTPS over apt


sudo apt install -y ca-certificates curl gnupg lsb-release

# Step 4: Add Docker’s official GPG key


sudo mkdir -p /etc/apt/keyrings
curl -fsSL [Link] | \
sudo gpg --dearmor -o /etc/apt/keyrings/[Link]

# Step 5: Set up the stable Docker repository


echo \
"deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/[Link]] \
[Link] \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/[Link].d/[Link] > /dev/null

# Step 6: Install Docker packages


sudo apt update
sudo apt install -y docker-ce docker-ce-cli [Link] docker-buildx-plugin docker-compose-plugin

# Step 7: Post-installation check


sudo docker version
sudo docker run hello-world

Optional: Run Docker Without sudo

sudo groupadd docker # May show 'already exists'

sudo usermod -aG docker $USER

newgrp docker

docker ps

Security Note: Adding your user to the Docker group gives root-equivalent access.

2.3 Docker Installation on macOS (Intel & Apple Silicon)

Docker runs on macOS through Docker Desktop, a packaged application that bundles the Docker
Engine, CLI, Docker Compose, and a lightweight Linux VM using Apple’s Hypervisor or QEMU.

Installation Steps:

1. Go to: [Link]

2. Download the correct installer:

o Apple Silicon (.dmg for M1/M2)

o Intel-based Mac (.dmg for x86_64)

3. Install by dragging the Docker icon to Applications

4. Launch Docker Desktop from Spotlight

5. Allow system permissions (if prompted)

6. Open Terminal and verify:


docker version

docker run hello-world

Common Issues on macOS

• macOS file permissions: Docker volumes may have permission denied errors; bind mount to
/Users/... paths only.

• VPN conflicts: Some VPNs interfere with Docker Desktop’s internal network.

• Rosetta emulation: M1 chips need compatible images; use multi-arch images.

2.4 Docker Installation on Windows (Windows 10/11 Pro)

Docker on Windows comes in two flavors:

• Docker Desktop – GUI interface + CLI, preferred for most users.

• Docker on WSL2 backend – Lightweight CLI-only installation, ideal for advanced users.

Installing Docker Desktop on Windows (GUI)

1. Install Windows Subsystem for Linux (WSL2) if not already present:

wsl --install

2. Enable virtualization in BIOS and Hyper-V in Windows Features

3. Download Docker Desktop from:


[Link]

4. Run the installer

5. After installation:

o It will set up WSL backend with a Linux distribution (usually Ubuntu)

o Start Docker Desktop from the Start Menu

6. Validate with PowerShell or CMD:

docker version

docker run hello-world

Advanced: Docker CLI Without Docker Desktop (Using WSL2)

1. Install WSL2 and Ubuntu from Microsoft Store

2. Inside Ubuntu (WSL), follow the Linux install instructions (same as 2.2)

3. Add Docker CLI path to Windows Environment Variable (optional)


Docker Inc. recently made Docker Desktop license-commercial. CLI + WSL method avoids that for
enterprise users.

2.5 Testing Docker Installation (Cross-Platform)

Regardless of OS, once Docker is installed, validate it using:

docker info # Shows runtime, drivers, cgroup version

docker version # Show CLI and daemon versions

docker run hello-world

Expected Output:

Hello from Docker!

This message shows that your installation appears to be working correctly.

2.6 Rootless Docker (Advanced)

Docker usually needs root privileges, but rootless mode is designed for security-constrained
environments.

Use Cases:

• Multi-user systems (e.g., university servers)

• CI/CD agents

• Running containers without exposing the host kernel directly

Setup on Linux:

# Step 1: Install Docker binaries


curl -fsSL [Link] | sh

# Step 2: Add binary path


export PATH=$HOME/bin:$PATH

# Step 3: Start the rootless daemon


systemctl --user start docker

# Step 4: Test
docker context use rootless
docker info | grep Rootless
Note: Some features (e.g., bind mounts to /) are restricted in rootless mode.
2.7 Post-Installation Security Recommendations

Security Task Command or Notes

Audit Docker group getent group docker

Disable TCP socket Avoid -H tcp://[Link]:2375

Protect Docker socket (/var/run/[Link]) Avoid mounting it into containers

Use secrets manager Use Docker Secrets / HashiCorp Vault

Enable AppArmor or SELinux Enforce policies via profile plugins

Disable legacy --privileged containers Reduces kernel attack surface

2.8 Docker Versions: CE vs EE vs Docker Engine

Type Description

Docker CE Community Edition – free, open-source, used by most developers

Docker EE Enterprise Edition – includes extra security, support, UCP/DTR (deprecated)

Moby Upstream open-source project for Docker Engine

Today, Docker CE is still the go-to edition for almost everyone, with Kubernetes handling
orchestration instead of EE's UCP.

2.9 Docker Desktop Alternatives (Advanced Use Cases)

Tool Use Case Comment

Compatible CLI (alias


Podman Rootless container runtime
docker=podman)

Docker Desktop alternative w/


Rancher Desktop Lightweight, GUI-based
Kubernetes

Colima Containers on macOS (w/ Lima VM) Open-source, faster on M1

Minikube +
Use Docker as K8s container runtime Good for testing K8s
Docker
2.10 Behind-the-Scenes: What Happens on docker run?

1. CLI sends request to Docker Daemon over UNIX socket

2. Daemon pulls image (if not cached)

3. Daemon creates container filesystem (layer union)

4. Allocates network namespace, PID namespace

5. Starts container with given entrypoint

6. Returns container ID to user

docker inspect <container_id> shows detailed JSON of configuration, mounts, IPs, etc.

2.11 Common Installation Problems & Fixes

Problem Fix

Docker daemon not running sudo systemctl start docker

“Cannot connect to Docker daemon” Check socket permission or sudo

WSL2 networking issues (Windows/macOS) Restart Docker Desktop, reconfigure

Proxy issues Set HTTP_PROXY and HTTPS_PROXY

"docker: command not found" Add Docker path to $PATH or reinstall


Module 3: Docker CLI
3.1 The Role of Docker CLI

The Docker CLI (docker) is the primary interface to communicate with the Docker Daemon (dockerd).
Every time you run a command like docker run, you're instructing the daemon to perform an action
like starting a container, pulling an image, or inspecting container metadata.

You’ll use the Docker CLI for:

• Creating and managing containers

• Building and tagging images

• Managing networks and volumes

• Logging, debugging, and monitoring

• Communicating with registries

• Orchestrating multi-container applications

3.2 Core Docker CLI Commands (Real-World Breakdown)

docker run – Run a New Container

Syntax:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

Example:

docker run -d -p 8080:80 nginx

Deep Dive:

Option Meaning

-d Detached mode (run in background)

-p Publish container port to host

--name Assign name to the container

--rm Automatically remove after exit

-e Set environment variables

-v Mount volumes
Option Meaning

--network Join container to a network

--entrypoint Override default ENTRYPOINT

--restart Restart policy (e.g., always, on-failure)

--cpu-shares, --memory Resource limits

Real-world:

docker run --name webapp \

-e NODE_ENV=production \

-v "$(pwd)"/app:/usr/src/app \

-p 3000:3000 \

node:20-alpine npm start

docker exec – Run Commands Inside a Running Container

Syntax:

docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

Examples:

docker exec -it myapp bash

docker exec mydb pg_isready

Real-world:

• Debug an app live.

• Check logs or configuration files inside.

• Run health checks manually.

-i, -t:

• -i: Interactive (keep STDIN open)

• -t: Allocate pseudo-TTY (terminal-like behavior)

docker ps – List Running Containers

Syntax:

docker ps [OPTIONS]
Examples:

docker ps

docker ps -a # All containers (including stopped)

docker ps -q # Only container IDs

docker ps --format '{{.Names}}: {{.Status}}'

Common fields:

• Container ID

• Image

• Command

• Created time

• Status

• Ports

• Names

docker stop / docker kill – Terminate Containers

Syntax:

docker stop [OPTIONS] CONTAINER

docker kill [OPTIONS] CONTAINER

Difference:

stop Sends SIGTERM → waits (default: 10s) → then SIGKILL

kill Immediately sends SIGKILL

docker rm – Remove Stopped Containers

docker rm container-name

docker rm $(docker ps -aq) # Remove all stopped

docker images – List Local Images

docker images

docker images -f dangling=true # Unused intermediary layers


docker rmi – Remove Images

docker rmi nginx

docker rmi $(docker images -q)

docker build – Build Docker Image from Dockerfile

Syntax:

docker build [OPTIONS] PATH | URL | -

Examples:

docker build -t devopsshack/webapp:1.0 .

docker build --no-cache --build-arg NODE_ENV=production .

Flags:

Option Meaning

-t Tag (name:tag)

--build-arg Pass arguments to Dockerfile

--no-cache Skip cache during build

--platform Target architecture (linux/arm64, linux/amd64)

docker inspect – Low-Level Container/Image Metadata

docker inspect container_id

docker inspect --format '{{.[Link]}}' container_id

Returns massive JSON including:

• Mounts

• Networks

• Config

• Volumes

• PID namespace

• Runtime stats
docker logs – Container Logs

docker logs container-name

docker logs -f --tail 100 myapp

Flag Purpose

-f Follow logs (live tail)

--tail Limit output to last N lines

docker stats – Live Resource Usage

docker stats

Monitors:

• CPU usage %

• Memory consumption

• Net I/O

• Block I/O

• PIDs

docker top – Show Running Processes in Container

docker top myapp

Like ps inside container – shows PID, CMD, CPU, memory per process.

docker system – Cleanups

docker system prune # Remove unused containers/images/networks

docker system df # Disk usage report

3.3 Docker Networking CLI

List networks:

docker network ls

Inspect network:

docker network inspect bridge


Create custom network:

docker network create mynet

Run containers on same network:

docker run --network=mynet --name app1 nginx

docker run --network=mynet --name app2 busybox ping app1

3.4 Docker Volumes & Bind Mounts via CLI

List volumes:

docker volume ls

Inspect volume:

docker volume inspect myvol

Create named volume:

docker volume create pgdata

Real-World Bind Mount:

docker run -v $(pwd)/code:/app myapp

Real-World Volume Use:

docker run -v pgdata:/var/lib/postgresql/data postgres

3.5 Docker Environments and Variables

Set Environment Variables in Container:

docker run -e ENV=production myapp

Use .env File:

docker --env-file ./[Link] run nginx

Access inside container:

printenv ENV

3.6 Restart Policies

Control how containers behave on host/system restart:

docker run --restart=always myapp


Meaning
Policy

no Default – don’t restart

on-failure Restart only on non-zero exit

always Always restart

unless-stopped Restart unless manually stopped

3.7 EntryPoint vs CMD – What Actually Runs?

In Dockerfile:

ENTRYPOINT ["node"]

CMD ["[Link]"]

Behavior:

• ENTRYPOINT = fixed executable

• CMD = arguments passed to ENTRYPOINT

You can override CMD with CLI:

docker run myapp [Link]

You can override ENTRYPOINT:

docker run --entrypoint "bash" myapp

3.8 Access and Permissions

Docker socket:

ls -l /var/run/[Link]

Add user to Docker group:

sudo usermod -aG docker $USER

Security warning: Docker group = root access.

3.9 Docker Pull, Push, and Tag

Pull image:

docker pull nginx:1.25


Tag local image:

docker tag myapp devopsshack/myapp:v1

Push to Docker Hub:

docker push devopsshack/myapp:v1

Must be logged in:

docker login

3.10 Advanced CLI: Debugging & Lifecycle

docker container inspect

Get mounted volumes, ports, health check info.

Health Checks:

In Dockerfile:

HEALTHCHECK CMD curl --fail [Link] || exit 1

Lifecycle states:

• Created

• Running

• Paused

• Restarting

• Exited

• Dead

Monitor with:

docker ps -a --format '{{.Names}} {{.Status}}'

3.11 Pro Tips & Patterns

• Use --rm for temporary debugging containers:

docker run --rm -it busybox sh

• Use --log-driver=none for silence:

docker run --log-driver=none myapp

• Restart container quickly:

docker restart container_name

• Use named containers:


docker run --name webserver nginx

• Combine docker ps with grep:

docker ps | grep nginx

Module 4: Dockerfile Mastery – Ultra Deep Dive

4.1 What Is a Dockerfile?

A Dockerfile is a text-based blueprint that defines how a Docker image should be built. It contains a
series of layered instructions that:

• Start from a base image

• Install dependencies

• Copy application code

• Define runtime configuration (e.g., ENV, ENTRYPOINT)

• Package it all into a repeatable, portable image

Think of it as infrastructure as code for containers.

4.2 Dockerfile Syntax: The Basic Building Blocks

Fundamental Instructions

Instruction Description

FROM Base image (must be first non-comment line)

RUN Execute shell commands while building image

COPY Copy files from host into image

ADD Similar to COPY but can also unpack archives or fetch remote URLs

CMD Default command when container starts

ENTRYPOINT Main executable of the container

ENV Set environment variables

ARG Build-time variables

WORKDIR Set working directory

EXPOSE Document ports the container listens on


Instruction Description

USER Change default user

VOLUME Mark mount points for external volumes

LABEL Metadata (author, version, etc.)

HEALTHCHECK Define health probe for container

SHELL Customize shell used in RUN (bash, sh, etc.)

4.3 Understanding Docker Image Layers

Every Dockerfile instruction (except some like ENV, LABEL, etc.) creates a new image layer.

Layers are:

• Cached to speed up future builds

• Immutable and stacked

• Shared across images (saves disk/network)

Example:

FROM ubuntu:20.04 # Layer 1

RUN apt update # Layer 2

RUN apt install -y curl # Layer 3

COPY . /app # Layer 4

Modify COPY . /app? You invalidate Layer 4 only, rest remain cached.

4.4 Real-World Optimized Dockerfile Example

Let’s say you're deploying a [Link] app:

# Stage 1: Builder

FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build


# Stage 2: Runtime

FROM node:20-alpine

WORKDIR /app

COPY --from=builder /app/dist ./dist

COPY --from=builder /app/node_modules ./node_modules

CMD ["node", "dist/[Link]"]

Why this is excellent:

Multi-stage → final image is lightweight


Avoids installing dev dependencies
Isolates build and run environments
Uses alpine base for smaller size
Explicit CMD entry point

4.5 Optimizing Build Performance

Layer Caching Tips

• Place least-changing layers first

• Always separate COPY package*.json from rest of code

• Use .dockerignore to skip unnecessary files

Use .dockerignore properly:

.git

node_modules

Dockerfile

*.log

coverage/

4.6 CMD vs ENTRYPOINT – Deep Dive

Feature CMD ENTRYPOINT

Default usage Define default arguments Define main executable

Overridable Yes (easily overridden) Yes, but requires --entrypoint

Combination Can be combined together ENTRYPOINT runs, CMD = args


Example:

ENTRYPOINT ["python3", "[Link]"]

CMD ["--help"]

You can run:

docker run myscript --version

Which runs:

python3 [Link] --version

4.7 Multi-Stage Builds (Advanced Pattern)

Why Multi-Stage?

• Keep final image minimal

• Strip out unnecessary dependencies/tools

• Build once, reuse everywhere

Real-World Example: Go Binary

# Stage 1: Build

FROM golang:1.21 AS builder

WORKDIR /src

COPY . .

RUN go build -o app

# Stage 2: Runtime

FROM [Link]/distroless/static

COPY --from=builder /src/app /app

ENTRYPOINT ["/app"]

Final image: Secure, non-root, ~15MB


No shell, no extra binaries, only the app
4.8 Advanced Dockerfile Patterns

Cleaning Up After RUN

RUN apt update && apt install -y \

curl \

&& rm -rf /var/lib/apt/lists/*

Keeps image size small by:

• Deleting cache

• Reducing unnecessary files

Build Arguments (ARG)

ARG NODE_VERSION=20

FROM node:${NODE_VERSION}-alpine

Set during build:

docker build --build-arg NODE_VERSION=18 .

Environment Variables (ENV)

ENV PORT=3000 NODE_ENV=production

Used inside container:

[Link]

Health Checks

HEALTHCHECK --interval=30s --timeout=5s \

CMD curl -f [Link] || exit 1

Adds status in docker inspect, helps orchestrators like Docker Swarm or Kubernetes.

Use Non-Root Users

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

USER appuser

Better security than running as root.


4.9 Distroless & Minimal Base Images

What is Distroless?

• No package manager, no shell, no glibc

• Only your app and runtime libraries

• Based on Google's secure containers

Example:

FROM golang:1.21 AS builder

WORKDIR /app

COPY . .

RUN go build -o server

FROM [Link]/distroless/base

COPY --from=builder /app/server /

ENTRYPOINT ["/server"]

Super secure
Cannot be SSH-ed into
Reduces attack surface

4.10 Common Mistakes & Anti-Patterns

Mistake Fix

Using latest tag Always pin exact version: node:20-alpine

Layer cache busting Avoid COPY . . too early

Keeping dev tools in image Use multi-stage to isolate

Missing .dockerignore Always include it

No cleanup in RUN Clean APT/YUM cache

Running as root Use USER for app execution


4.11 Debugging Builds

Build with verbose output:

DOCKER_BUILDKIT=1 docker build .

View intermediate images:

docker history image-id

Run image with shell:

docker run -it --entrypoint sh myimage

4.12 Real-World Dockerfile Templates

1. [Link]

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./

RUN npm ci --only=production

COPY . .

CMD ["node", "[Link]"]

2. Python Flask

FROM python:3.11-slim

WORKDIR /app

COPY [Link] ./

RUN pip install -r [Link]

COPY . .

ENV FLASK_APP=[Link]

CMD ["flask", "run", "--host=[Link]"]

3. Spring Boot

FROM eclipse-temurin:17-jdk

COPY target/[Link] /[Link]

ENTRYPOINT ["java", "-jar", "/[Link]"]


4. .NET Core

FROM [Link]/dotnet/aspnet:7.0

COPY ./publish /app

WORKDIR /app

ENTRYPOINT ["dotnet", "[Link]"]

5. NGINX Static Site

FROM nginx:1.25-alpine

COPY ./static /usr/share/nginx/html

4.13 Dockerfile Best Practices

• Pin versions (python:3.11-slim, node:20-alpine)

• Multi-stage builds for clean separation

• Reduce number of layers (combine RUN)

• Use .dockerignore aggressively

• Never hardcode secrets

• Avoid ADD unless needed (use COPY)

• Always validate with docker build . && docker run


Module 5: Docker Volumes & Storage
5.1 Why Persistent Storage Matters in Docker

By default, when a container stops or is deleted:

• All data stored inside the container’s writable layer is lost.

• Containers are ephemeral — but your data shouldn’t be.

Persistent storage is critical when:

• Running databases like PostgreSQL, MySQL, MongoDB

• Saving logs, user uploads, configuration files

• Sharing data between multiple containers

• Retaining build artifacts (CI/CD pipelines)

Solution: Volumes and Bind Mounts.

5.2 Types of Docker Storage Mechanisms

Type Description Use Case

Bind Mounts Maps host directory to container Full control, local dev

Named Volumes Managed by Docker itself Production-grade, portable

tmpfs Mounts In-memory storage, non-persistent Speed-critical temp storage

Volume Plugins External storage backends NFS, Amazon EFS, Portworx

5.3 Bind Mounts – Full Control, High Flexibility

Syntax:

docker run -v /host/path:/container/path image

Example:

docker run -v $(pwd)/code:/app myapp

Pros:

• Easy to edit files from host

• Useful in development

• No Docker volume needed


Cons:

• Not portable across machines

• Security risk if host directory is compromised

• Performance varies depending on OS

5.4 Named Volumes – Portable, Managed by Docker

Syntax:

docker volume create myvol

docker run -v myvol:/app/data myapp

Inspect Volume:

docker volume inspect myvol

Pros:

• Managed entirely by Docker

• More secure (path is isolated)

• Easy to back up and restore

• Preferred for databases and production

Cons:

• Opaque to the user (can’t directly edit without Docker)

• Limited to local storage unless using a plugin

5.5 Differences: Bind Mount vs Named Volume

Feature Bind Mount Named Volume

Managed by You (host filesystem) Docker

Path Explicit /path on host Automatically in /var/lib/docker/volumes

Portability Low High

Use in Dev High Moderate

Use in Prod Risky Preferred

Performance Variable (esp. on macOS) Optimized


5.6 Example: PostgreSQL with Named Volume

docker volume create pgdata

docker run -d \

--name pg \

-e POSTGRES_PASSWORD=mysecret \

-v pgdata:/var/lib/postgresql/data \

postgres:15

Inspect Volume:

docker volume inspect pgdata

Backup Volume:

docker run --rm -v pgdata:/data -v $(pwd):/backup alpine \

tar czf /backup/[Link] -C /data .

Restore Volume:

docker run --rm -v pgdata:/data -v $(pwd):/backup alpine \

tar xzf /backup/[Link] -C /data

5.7 Data Sharing Between Containers

Shared Volume:

docker volume create sharedvol

docker run -d --name app1 -v sharedvol:/shared busybox tail -f /dev/null

docker run -d --name app2 -v sharedvol:/shared busybox tail -f /dev/null

Both app1 and app2 can access /shared and see the same files.

5.8 tmpfs Mounts – Temporary RAM Storage

docker run --tmpfs /cache:rw,size=100m alpine

Used when:

• You want data only during container runtime

• You need ultra-fast access (RAM-backed)


• You are dealing with sensitive data you want gone after shutdown

5.9 Docker Volume Plugins (Advanced)

Want to store Docker volumes remotely or on specialized infrastructure?

Use volume plugins:

Plugin Use Case

local Default driver

nfs Mount from NFS

rexray/ebs AWS EBS

glusterfs Scalable file system

azurefile Azure File Shares

Example: Using NFS Plugin

docker volume create \

--driver local \

--opt type=nfs \

--opt o=addr=[Link],rw \

--opt device=:/nfs/data \

nfsvolume

Then mount:

docker run -v nfsvolume:/mnt alpine

5.10 Inspecting and Managing Volumes

docker volume ls # List all volumes

docker volume inspect myvol # View metadata

docker volume rm myvol # Delete volume

docker system df # Show disk usage


5.11 Common Mistakes with Volumes

Mistake Problem

Using bind mount to host-sensitive path Could overwrite system files

Forgetting to use volume in DB container Data lost when container deleted

Assuming docker rm deletes volume It doesn’t unless --volumes used

Mounting read-only volumes as writable App will crash on write

Not using .dockerignore with bind mount Can lead to large unwanted context copies

5.12 Practical Volume Management Patterns

Development Workflow:

• Use bind mounts to code:

docker run -v $(pwd):/app node npm run dev

Production Workflow:

• Use named volumes:

docker volume create logs

docker run -v logs:/var/log/nginx nginx

Volume Lifecycle:

1. docker volume create

2. Attach to container

3. Optional backup/restore

4. docker volume rm to clean up

5.13 Security Implications of Volumes

• Never mount / or host root paths

• Protect host paths via AppArmor/SELinux

• Avoid exposing the Docker socket (/var/run/[Link]) as volume

• Don’t mount secrets unencrypted


5.14 Real-World Use Cases

Dev Environment with Live Code Reloading

docker run -v $(pwd)/src:/app -v /app/node_modules myapp nodemon [Link]

CI/CD Caching Volumes

docker volume create cache

docker run -v cache:/root/.npm myapp npm install

Database Persistence

docker volume create mongo_data

docker run -v mongo_data:/data/db mongo

Backups and Migration

docker run --rm -v myvol:/vol -v $(pwd):/backup alpine \

tar czf /backup/[Link] -C /vol .

5.15 Docker Compose & Volumes

version: "3.9"

services:

db:

image: postgres

volumes:

- pgdata:/var/lib/postgresql/data

app:

build: .

volumes:

- .:/app

volumes:

pgdata:
Module 6: Docker Networking
6.1 Why Networking in Docker Matters

Every modern application is network-dependent. Whether you're:

• Connecting a backend to a database

• Exposing an API to the public

• Orchestrating microservices

Docker provides a powerful virtual networking layer that lets containers:

• Talk to each other securely

• Map internal ports to host ports

• Isolate environments

• Emulate production-like topologies locally

6.2 Types of Docker Networks

Network
Description Use Case
Type

bridge Default, per-host container network Simple container-to-container

Bypass Docker’s virtual network, share host


host High-performance, single-node
network stack

none Disable networking entirely Isolated, sandboxed containers

overlay Multi-host networking (Swarm only) Cross-node communication

Legacy systems, custom network


macvlan Containers get direct IPs on LAN
setups

6.3 Bridge Network (Default Mode)

When you run a container without specifying a network, Docker connects it to the default bridge
network.

View default networks:

docker network ls
NETWORK ID NAME DRIVER SCOPE

a1b2c3d4e5f6 bridge bridge local

Inspect:

docker network inspect bridge

• Containers are assigned a private IP (e.g., 172.17.0.X)

• DNS is available inside network

• You cannot resolve containers by name on default bridge — use custom bridge for that

6.4 Custom Bridge Networks (Named Networking)

Why use them?

• Enables DNS-based container discovery

• Easier service-to-service communication

• Clean separation between environments

Create custom network:

docker network create mynet

Test example:

docker run -d --name app1 --network=mynet nginx

docker run -it --rm --network=mynet busybox ping app1

PING app1 ([Link]): 56 data bytes

6.5 Host Networking

In host networking, Docker container uses the host’s network namespace — no isolation.

docker run --network host nginx

Important:

• Only available on Linux

• Containers share host IP and port space

• Cannot run multiple containers on same port

Use case:

• Ultra-low-latency services
• Performance testing

• Avoiding NAT

6.6 None Network – Full Isolation

docker run --network none alpine

• No network access (not even loopback)

• Ideal for security isolation, sandboxed workloads

6.7 Overlay Network (Multi-Host)

Overlay networks enable containers running on different hosts to talk to each other. Built-in with
Docker Swarm.

docker network create \

--driver overlay \

--attachable \

myoverlay

Requirements:

• Docker Swarm initialized

• Hosts can communicate via ports: 2377, 7946, 4789

6.8 Port Mapping and Exposure

Syntax:

docker run -p <host_port>:<container_port>

Example:

docker run -d -p 8080:80 nginx

• Exposes container’s port 80 on host’s port 8080

• Accessible at [Link]

Advanced:

docker run -p [Link]:9000:80 nginx # Bind to localhost only

docker run -p [Link]:3000:3000 # Bind to specific host IP


6.9 Docker DNS and Service Discovery

Containers connected to the same user-defined bridge or overlay network can resolve each other by
container name.

Example:

docker run --name redis --network appnet redis

docker run --rm --network appnet busybox ping redis

DNS resolves:

plaintext

CopyEdit

PING redis ([Link])

6.10 Inspecting Container Networks

Inspect container:

docker inspect container-name

Look under "Networks" section for:

• IP Address

• MAC

• Network name

• Gateway

6.11 Connecting Containers to Multiple Networks

docker network create frontend

docker network create backend

docker run -d --name app1 --network frontend nginx

docker network connect backend app1

Now, app1 is connected to both frontend and backend.

6.12 Docker Compose Networking

Docker Compose automatically:

• Creates a default network


• Adds services to that network

• Enables name-based resolution

Example:

version: "3"
services:
web:
image: nginx
app:
build: .
depends_on:
- web

Here, app can connect to web using just:

curl [Link]

6.13 Security and Isolation in Docker Networks

Feature Available in Docker?

Network segmentation via user-defined networks

Internal-only ports use --internal flag

Encrypted overlay in Swarm only

Firewalling between containers (not native, needs external tools like Cilium)

6.14 Real-World Microservices Example

docker network create appnet

docker run -d --name api --network appnet myapi

docker run -d --name redis --network appnet redis

docker run -d --name nginx --network appnet -p 80:80 nginx

• myapi talks to redis at hostname redis

• nginx talks to api at hostname api

6.15 Network Troubleshooting

“Cannot reach container”:


• Not on same network?

• Wrong container name (DNS)

• Firewall on host?

Tools:

docker exec -it container ping another

docker exec -it container nslookup other

docker exec -it container curl [Link]

6.16 Clean Up and Manage Networks

List:

docker network ls

Inspect:

docker network inspect mynet

Remove:

docker network rm mynet

Prune:

docker network prune


Module 7: Docker Compose
7.1 What is Docker Compose?

Docker Compose is a tool used to define and manage multi-container Docker applications.

Rather than running multiple docker run commands, Compose allows you to:

• Define services, networks, volumes in a single YAML file

• Start all services with docker-compose up

• Stop, rebuild, scale, or update them with simple commands

It acts as orchestration for development and testing, perfect for:

• Microservices stacks

• Full-stack development

• CI/CD environments

• Integration testing

7.2 Key Concepts in Compose

services

Defines each container (e.g., app, database, cache).

volumes

Data persistence across containers.

networks

Enables inter-container communication.

build

Defines how to build images directly from Dockerfiles.

depends_on

Sets startup ordering (not health-based).


7.3 Compose File Format (Version 3.x)

version: "3.9"

services:

app:

build: .

ports:

- "8080:80"

environment:

- NODE_ENV=production

db:

image: postgres:15

volumes:

- pgdata:/var/lib/postgresql/data

volumes:

pgdata:

7.4 Real-World Full Stack Example ([Link] + PostgreSQL)

version: "3.8"

services:

web:

build: ./web

ports:

- "3000:3000"

depends_on:

- db

db:

image: postgres:15

environment:

POSTGRES_USER: user

POSTGRES_PASSWORD: pass

POSTGRES_DB: mydb
volumes:

- dbdata:/var/lib/postgresql/data

volumes:

dbdata:

Web connects to the DB using db as hostname.

7.5 Common docker-compose Commands

Command Description

docker-compose up Build and run all services

docker-compose up -d Run in background

docker-compose down Stop and remove containers, networks

docker-compose build Build/rebuild services

docker-compose logs View aggregated logs

docker-compose exec <svc> Run commands inside container

docker-compose ps List containers

docker-compose restart Restart services

7.6 Service Configuration Options

Key Purpose

image Pull from Docker Hub

build Build from Dockerfile

ports Port mapping

volumes Volume binding

environment Set environment variables

networks Attach to Docker networks

restart Restart policies

command Override default command


Key Purpose

entrypoint Override entrypoint

healthcheck Define service health

7.7 Build Contexts

web:

build:

context: ./app

dockerfile: [Link]

Supports:

• Custom Dockerfile paths

• Build args

args:

NODE_ENV: production

7.8 Using .env Files in Compose

POSTGRES_USER=admin

POSTGRES_PASSWORD=secret

Compose automatically loads .env in the same directory.

Or use:

docker-compose --env-file [Link] up

7.9 Advanced Volumes and Bind Mounts

Bind Mount:

volumes:

- ./code:/app

Named Volume:

volumes:

- pgdata:/var/lib/postgresql/data
External Volume:

volumes:

pgdata:

external: true

7.10 Custom Networks

networks:

appnet:

driver: bridge

Attach services:

services:

backend:

networks:

- appnet

Compose gives service-name-based DNS by default.

7.11 depends_on vs Health Check

depends_on:

- db

Does not wait for db to be healthy, only checks container start.

To wait for healthy state, use:

healthcheck:

test: ["CMD", "pg_isready", "-U", "postgres"]

interval: 10s

timeout: 5s

retries: 5

And use wait-for-it or similar in app container entrypoint.


7.12 Override Compose for Dev/Prod

Default file:

[Link]

Override:

[Link]

Or custom:

docker-compose -f [Link] -f [Link] up

Use Case:

• dev: bind mounts, live reload

• prod: use prebuilt images, healthchecks

7.13 Compose in CI/CD Pipelines

In GitHub Actions:

- uses: docker/setup-buildx-action@v2

- run: docker-compose -f [Link] up --abort-on-container-exit

In GitLab CI:

services:

- docker:dind

script:

- docker-compose up -d

- docker-compose exec app npm test

7.14 Secrets Management with Compose

secrets:

my_secret:

file: ./secrets/db_password.txt

Attach to service:

yaml

CopyEdit

services:

db:
secrets:

- my_secret

Inside container: /run/secrets/my_secret

Requires Docker Swarm mode.

7.15 Rebuild and Restart Patterns

Rebuild with cache:

docker-compose build

Rebuild fresh:

docker-compose build --no-cache

Force recreate:

docker-compose up --force-recreate

7.16 Logging and Debugging

docker-compose logs app

docker-compose logs -f db

Troubleshooting tip:

docker-compose config

This shows fully merged config (default + override).

7.17 Real-World Microservice Example

Stack:

• NGINX as reverse proxy

• [Link] app

• MongoDB

• Redis

version: "3.8"

services:

api:

build: ./api

ports: [ "3000:3000" ]
depends_on: [ mongo, redis ]

mongo:

image: mongo

volumes: [ "mongodata:/data/db" ]

redis:

image: redis

nginx:

image: nginx

ports: [ "80:80" ]

volumes:

- ./[Link]:/etc/nginx/[Link]

volumes:

mongodata:

7.18 Compose vs Kubernetes (K8s)

Feature Docker Compose Kubernetes

Use Case Dev/Test/CI Production orchestration

Portability Local + CI/CD Multi-cloud

Networking Built-in DNS Advanced + Service Mesh

Scaling Manual (via CLI) Auto-scaling

Health Checks Basic Full readiness/liveness

Secrets Swarm-only Native support

Module 8: Docker in CI/CD Pipelines (Ultra Deep Dive)

8.1 Why Use Docker in CI/CD?

Docker revolutionizes CI/CD by enabling:

• Isolated, reproducible builds across environments

• Fast spin-up and teardown of testing environments


• Immutable artifacts for production

• Seamless packaging + deployment in the same workflow

Docker ensures “works on my machine” == “works in CI == works in production.”

8.2 Typical Docker-Enabled Pipeline Phases

1. Checkout Code

2. Build Image (docker build)

3. Run Tests inside Container

4. Scan Image for Vulnerabilities

5. Sign & Push to Registry

6. Deploy to Staging/Prod

8.3 Example CI/CD Pipeline Workflow with Docker

Git Push →

CI Pipeline →

[docker build]

[docker run tests]

[docker scan]

[docker tag & push]

[trigger deploy]

8.4 Docker in Jenkins Pipelines (Declarative)

Jenkinsfile Example:

pipeline {

agent any

environment {

IMAGE = "myapp:${env.BUILD_ID}"

stages {

stage('Build') {

steps {
sh 'docker build -t $IMAGE .'

stage('Test') {

steps {

sh 'docker run --rm $IMAGE npm test'

stage('Scan') {

steps {

sh 'trivy image $IMAGE'

stage('Push') {

steps {

withCredentials([usernamePassword(credentialsId: 'dockerhub', passwordVariable: 'PASS',


usernameVariable: 'USER')]) {

sh '''

echo "$PASS" | docker login -u "$USER" --password-stdin

docker tag $IMAGE myrepo/myapp:$BUILD_ID

docker push myrepo/myapp:$BUILD_ID

'''

}
8.5 Docker in GitHub Actions

.github/workflows/[Link]

name: Build & Push Docker

on:
push:
branches: [ main ]

jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Login to DockerHub


uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PASS }}

- name: Build and Push Image


uses: docker/build-push-action@v4
with:
push: true
tags: devopsshack/app:latest

8.6 Docker in GitLab CI

image: docker:20.10
services:
- docker:dind

stages:
- build
- test
- push

variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

build:
stage: build
script:
- docker build -t $IMAGE_TAG .

test:
stage: test
script:
- docker run --rm $IMAGE_TAG npm test

push:
stage: push
script:
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin
$CI_REGISTRY
- docker push $IMAGE_TAG

8.7 Test Strategies Inside Docker

Strategy Example

Unit tests docker run --rm myimage npm test

Integration tests Use docker-compose to spin up DB + app

Parallel tests Run multiple containers concurrently

End-to-End (E2E) Cypress/Selenium with headless browser inside container

8.8 Security: Scan Images in CI

Use tools like:

• Trivy

• Grype

• Docker Scout

• Snyk

Example:

trivy image devopsshack/app:latest

Add into CI job for vulnerability scanning. Fail build on CVEs above threshold.

8.9 Secrets in Docker CI

Never hardcode secrets in Dockerfile or CI YAML.

Use:

• GitHub Secrets

• Jenkins Credentials plugin

• GitLab CI/CD Variables

In Dockerfile, use build args instead of ENV for temporary secrets:


ARG SECRET

RUN echo $SECRET | some_command

Build with:

bash

CopyEdit

docker build --build-arg SECRET=value .

But prefer to inject secrets at runtime, not bake into image.

8.10 Reproducible Docker Builds in CI

Key practices:

• Pin exact base image version (python:3.11.3-slim)

• Use multi-stage builds

• Use --platform for architecture-specific builds

• Leverage BuildKit for caching and cross-platform support

DOCKER_BUILDKIT=1 docker build --platform linux/amd64 .

8.11 Accelerate CI with Docker Caching

Enable build cache:

• GitHub Actions: build-push-action with caching

• Jenkins: use persistent workspace layers

• Use --cache-from if previous images are available in registry

docker build --cache-from=devopsshack/app:latest -t app:v2 .

8.12 SBOM Generation in CI

Software Bill of Materials:

• List of packages in your image

• Required for compliance (e.g., supply chain audits)

Use:

syft devopsshack/app:latest -o json > [Link]

Validate:
cosign attest --predicate [Link] ...

8.13 Containerized CI Runners

Run CI runners inside containers for isolated job execution.

Example with GitHub self-hosted runners in Docker:

docker run -d \

-e RUNNER_TOKEN=... \

-e REPO_URL=[Link] \

myrunner-image

8.14 Container-Based Deployment

CI can:

• Tag and push image

• Trigger deployment to:

o Kubernetes (kubectl, helm, ArgoCD)

o ECS (Fargate)

o Nomad

o Docker Swarm

Tagging example:

docker tag myapp myrepo/myapp:1.0.${BUILD_ID}

docker push myrepo/myapp:1.0.${BUILD_ID}

8.15 End-to-End Example: Dockerized Build + Deploy

- Checkout

- Build Docker image

- Run tests inside container

- Scan with Trivy

- Sign with Cosign

- Push to DockerHub

- Trigger ArgoCD sync


Module 9: Docker Security in Depth
9.1 Why Docker Security Matters

Containers may feel isolated, but they:

• Share the same OS kernel

• Can be misconfigured to escalate privileges

• May carry vulnerable libraries or secrets

Without proper security, Docker becomes an attack vector — not just an abstraction.

You must secure:

• The Docker daemon

• Container images

• Container runtime

• Secrets

• Build pipelines

• The host system

9.2 Security Boundaries: VM vs Container

Factor Virtual Machines Containers

Kernel isolation Strong (hypervisor) Weak (shared kernel)

Privilege Escalation Difficult (hardware level) Easier if container breaks out

Attack Surface Full OS per VM Shared kernel, thin abstraction

One bad container could compromise the host if improperly configured.

9.3 Docker Daemon Hardening

Do not expose Docker socket over TCP

# BAD: exposes API to the world

dockerd -H tcp://[Link]:2375
Restrict access to /var/run/[Link]

ls -l /var/run/[Link]

• Add users carefully to docker group:

sudo usermod -aG docker <user>

Best Practice: Only run docker commands as trusted users.

9.4 Rootless Docker (Better Isolation)

Run Docker without root:

curl -fsSL [Link] | sh

export PATH=$HOME/bin:$PATH

[Link] install

Benefits:

• Daemon runs as normal user

• No sudo access required

• Limits kernel attack surface

9.5 Avoid --privileged Containers

docker run --privileged alpine

This:

• Gives full access to host kernel capabilities

• Mounts host devices

• Completely bypasses container isolation

NEVER use in production unless absolutely required (e.g., Docker-in-Docker).

9.6 Least Privilege Container Execution

Drop capabilities:

docker run --cap-drop=ALL myapp

Or:

--cap-drop=NET_RAW --cap-add=CHOWN
Read-only file systems:

docker run --read-only myapp

Non-root users:

RUN adduser -D appuser

USER appuser

9.7 Hardened Base Images

Use:

• Distroless base images

• Minimal OS (e.g., Alpine)

• No package managers or shell

Examples:

FROM [Link]/distroless/static

No bash, no curl, no package managers → harder to exploit.

9.8 Image Vulnerability Scanning

Use:

• Trivy

• Grype

• Snyk

• Docker Scout

Example:

trivy image devopsshack/app:latest

Check for:

• OS-level vulnerabilities (Alpine, Debian)

• Language-specific (npm, pip, gem)

• Misconfigurations
9.9 Software Bill of Materials (SBOMs)

Generate SBOM:

syft devopsshack/app:latest -o json > [Link]

Validate it:

cosign attest --predicate [Link] ...

Required by many security compliance standards (NIST, SLSA).

9.10 Image Signing and Verification (Cosign)

Sign image:

cosign sign --key [Link] devopsshack/app:latest

Verify:

cosign verify --key [Link] devopsshack/app:latest

Use GitHub Actions + Cosign to:

• Sign images after CI build

• Validate before deployment

9.11 Secrets Management

Anti-patterns:

ENV AWS_SECRET_KEY=mysecret

Best Practices:

• Use docker secrets (Swarm mode)

• Inject via environment variables in CI (docker run -e)

• Use runtime secrets manager like:

o HashiCorp Vault

o AWS Secrets Manager

o Doppler

9.12 Security Labels and Metadata

Add metadata to images:

LABEL maintainer="devopsshack"

LABEL [Link]="[Link]
Useful for traceability, SBOM tooling, security scanners.

9.13 Enforce Runtime Profiles

SELinux or AppArmor:

docker run --security-opt apparmor=docker-default myapp

Seccomp:

docker run --security-opt seccomp=[Link] myapp

These reduce syscall attack surface.

9.14 Docker Bench for Security (Audit Tool)

Run full audit on Docker host:

git clone [Link]

cd docker-bench-security

sudo ./[Link]

Scans for:

• Docker daemon config issues

• Container runtime risks

• Host OS hardening gaps

9.15 Container Network Isolation

• Use custom bridge networks

• Avoid containers on default bridge or host

• Use firewalls to restrict egress/ingress

• Add --internal flag to networks to prevent external access:

docker network create --internal isolated

9.16 Runtime Monitoring and Auditing

Tools:

• Falco (syscall detection)

• Sysdig Secure

• Docker Scout
• KubeArmor (for Kubernetes)

Monitor:

• Unexpected execs (e.g., /bin/bash)

• File access to sensitive paths

• Outbound network spikes

9.17 CI/CD Security Controls

• Run builds in isolated Docker container

• Scan images before pushing

• Sign images

• Store images in private registries

• Require multi-factor auth on registry access

• Validate image at deploy-time

9.18 Secure Private Docker Registries

Use:

• Authentication (basic auth, LDAP, OIDC)

• HTTPS with TLS certificates

• Scan registry images periodically

• Rate limit pull/push

• Enable auditing

Self-hosted options:

• Harbor

• JFrog Artifactory

• GitHub Container Registry (GHCR)

• AWS ECR, GCP Artifact Registry


9.19 Defense in Depth Summary

Layer Protection

Docker Daemon Disable remote access, rootless mode

Images Sign, scan, use minimal base

Runtime Drop capabilities, run non-root

Network Isolate, segment, firewall

Secrets Inject securely, avoid baking in

Registry Secure access, scan continuously

CI/CD Scan, sign, verify pre-deploy


Module 10: Docker Internals & Advanced Concepts
10.1 What Actually Happens When You Run a Container?

When you execute:

docker run -it ubuntu bash

Here’s what Docker does internally:

1. CLI → Docker API:


docker CLI sends a request to Docker daemon (dockerd) via the UNIX socket
(/var/run/[Link]).

2. Pulls Image:
Docker checks if ubuntu exists locally. If not, it pulls from Docker Hub.

3. Creates Container:

o Allocates a container ID

o Sets up an isolated root filesystem

o Initializes namespaces (PID, NET, MNT, etc.)

o Applies cgroups (resource limits)

4. Starts Container:

o Launches container process (here, bash) inside the new environment

o Logs, IO, and runtime metrics begin streaming

10.2 Linux Kernel Features Behind Containers

Docker is just a user-friendly API over core Linux kernel primitives:

1. Namespaces

Provide isolation between containers:

Namespace Isolates

pid Process IDs

mnt Mount points

net Network stack

uts Hostname, domain

ipc Interprocess comm.

user UID/GID mappings


Namespace Isolates

cgroup Control groups

Each container has its own namespaces.

2. Cgroups (Control Groups)

Control resource usage:

• CPU shares, quotas

• Memory limits

• Disk I/O limits

• Max processes

View via:

cat /proc/self/cgroup

3. Union File Systems

Docker layers images using UnionFS:

• Combines multiple read-only image layers with a top read-write layer

• Only changes go into the writable layer

Common drivers:

• overlay2 (default on most systems)

• aufs, btrfs, zfs (legacy)

Inspect with:

docker info | grep Storage

10.3 Container Lifecycle Internals

Phase Internals

create Allocates container ID, filesystem, sets config

start Spawns entrypoint process in isolated namespaces

stop Sends SIGTERM, then SIGKILL after grace period

pause Freezes process via cgroup freezer


Phase Internals

kill Sends signal (SIGKILL, etc.) to container PID

remove Deletes filesystem, container metadata

10.4 Docker Architecture Refresher

[ docker CLI ]

[ Docker API ]

[ Docker Daemon (dockerd) ]

[ containerd ] ← manages container lifecycle

[ runc ] ← low-level OCI-compliant runtime

[ Linux Kernel ]

• runc implements the OCI runtime spec

• containerd handles execution, image management, metrics

Docker is not the only player: Kubernetes, CRI-O, and Podman also use containerd + runc.

10.5 BuildKit – The Modern Docker Build Engine

BuildKit is a modern backend for docker build.

Advantages:

• Parallelism

• Layer caching

• Secrets injection

• Better output control

Enable:

export DOCKER_BUILDKIT=1

docker build .
Secrets Example:

# syntax=docker/dockerfile:1.4

RUN --mount=type=secret,id=token curl -H "Authorization: $(cat /run/secrets/token)"


[Link]

10.6 DevContainers (Visual Studio Code + Docker)

DevContainers are containerized dev environments used via VSCode Remote Containers or GitHub
Codespaces.

[Link] Example:

"name": "Node Dev",

"build": {

"dockerfile": "Dockerfile",

"args": { "VARIANT": "16" }

},

"settings": {

"[Link]": "/bin/bash"

},

"extensions": [

"[Link]-eslint",

"[Link]-vscode"

Benefits:

• Same environment for everyone on the team

• Works on any machine with Docker + VSCode

• Prevents “works on my laptop” bugs

10.7 Docker Contexts and Remote Management

Use docker context to manage multiple environments:

docker context create remote \

--docker "host=ssh://user@remote-host"
docker context use remote

docker ps

Great for:

• Managing remote Linux servers

• Switching between local and cloud environments

• Running builds on a remote BuildKit daemon

10.8 Inspecting Docker Internals (Hands-on)

Get container PID:

docker inspect --format '{{.[Link]}}' mycontainer

Then:

nsenter -t <PID> -a

You are now “inside” the container from the host.

View namespace info:

ls -l /proc/$(docker inspect ... | jq .[].[Link])/ns

View image layers:

docker history <image>

10.9 Namespaces: Isolating Everything

Isolate hostname:

docker run -it busybox hostname

Inside: container_id, not host hostname.

Isolate PIDs:

docker run -it busybox sh

ps aux

You’ll only see a few processes inside container.

10.10 Image Layer Optimization Strategy


Tip Benefit

Minimize RUN instructions Fewer layers, faster builds

Sort commands by volatility Better caching

Use .dockerignore Smaller context

Use COPY package*.json before COPY . . Faster build if code changes

Use multi-stage builds Clean final image

10.11 Advanced Build Patterns

Build for multiple platforms:

docker buildx create --use

docker buildx build --platform linux/amd64,linux/arm64 \

-t myapp:multiarch --push .

Use target stages:

FROM node AS build

RUN npm ci && npm run build

FROM nginx

COPY --from=build /dist /usr/share/nginx/html

Then build only up to build stage:

docker build --target build .

10.12 OCI (Open Container Initiative) Standards

Docker pushed for standardization → led to OCI specs:

Spec Purpose

OCI Runtime Spec How containers are started and run

OCI Image Spec How images are structured and layered

OCI Distribution Spec How registries store, fetch images

Runtimes like:

• Docker
• Podman

• CRI-O
All adhere to OCI → compatible across ecosystems.

10.13 Container Performance Metrics

Use tools like:

• docker stats

• cAdvisor

• containerd metrics

• sysdig

Monitor:

• CPU usage

• Memory footprint

• Network I/O

• Block I/O

Containers can be resource-hungry if not tuned (watch OOMKilled events).


Module 12: Real-World Use Cases for Docker
12.1 Why Use Docker in Real Projects?

Docker enables:

• Reproducibility of environments

• Isolation between services

• Portability across systems

• Developer onboarding in minutes

• Infrastructure consistency in CI/CD

• Fast provisioning in staging/prod

Let’s explore real-world examples in dev, testing, CI/CD, microservices, and cloud-native
architectures.

12.2 Use Case: Containerizing a Monolithic App

Imagine a legacy Java-based Spring Boot application.

Traditional Deployment:

• Java version mismatch

• Dependency hell

• OS-specific bugs

Dockerized Solution:

FROM eclipse-temurin:17-jdk

COPY target/[Link] /[Link]

CMD ["java", "-jar", "/[Link]"]

Benefits:

• Runs identically across environments

• Java version is locked and consistent

• Simplifies deployment using a single .jar image

12.3 Use Case: Microservices Architecture

A modern web stack may have:

• frontend (React/Vue)
• api-gateway

• auth-service

• user-service

• order-service

• redis, postgres

Compose-based Dev Environment:

version: "3.9"

services:

api:

build: ./api

depends_on: [ db, redis ]

environment:

DB_HOST: db

db:

image: postgres

volumes: [ "dbdata:/var/lib/postgresql/data" ]

redis:

image: redis

frontend:

build: ./frontend

ports: [ "3000:3000" ]

volumes:

dbdata:

Benefits:

• Easy to run full stack locally

• Inter-service DNS resolution (db, redis)

• Shared networks and volumes

12.4 Use Case: CI/CD Testing

Use Docker to isolate:

• Unit test environments


• Integration tests with databases

• E2E tests using Cypress or Selenium

Example: Node Test Runner

test:

image: node:20

volumes:

- .:/app

working_dir: /app

command: npm test

Can be triggered via:

bash

CopyEdit

docker-compose run test

12.5 Use Case: Secure Build Pipelines

In CI:

• Build Docker image

• Run unit tests

• Run Trivy scan

• Push image to registry

• Trigger deployment

GitHub Actions Snippet:

- uses: docker/build-push-action@v4

with:

context: .

push: true

tags: myorg/app:latest

- name: Scan

run: trivy image myorg/app:latest


12.6 Use Case: Cloud Portability

A single Docker image can:

• Run locally with Docker Desktop

• Be deployed on:

o AWS ECS/Fargate

o GCP Cloud Run

o Azure App Service

o Kubernetes

Example:

docker build -t devopsshack/app:v1 .

docker push devopsshack/app:v1

Then deploy on any platform using the same image.

12.7 Use Case: Running Databases Locally

Running MySQL or PostgreSQL natively is messy. Use Docker:

docker run --name pg \

-e POSTGRES_PASSWORD=secret \

-v pgdata:/var/lib/postgresql/data \

-p 5432:5432 postgres:15

Benefits:

• Version-controlled DB

• Easy reset

• No polluting local system

12.8 Use Case: Teaching, Demos & Developer Onboarding

Using DevContainers:

"name": "Python Starter",

"build": { "dockerfile": "Dockerfile" },

"extensions": ["[Link]"]

}
VSCode loads a full dev environment on any machine via Docker.

Benefits:

• Instant reproducibility

• No “works on my laptop”

• Faster onboarding

12.9 Use Case: Backups and Data Migration

Use volumes + tarball backups:

docker run --rm \

-v pgdata:/data \

-v $(pwd):/backup \

alpine tar czf /backup/[Link] -C /data .

Restore:

bash

CopyEdit

docker run --rm \

-v pgdata:/data \

-v $(pwd):/backup \

alpine tar xzf /backup/[Link] -C /data

12.10 Use Case: Dev/Test Matrix Across Platforms

Use multi-arch builds:

docker buildx build \

--platform linux/amd64,linux/arm64 \

-t myorg/app:multiarch \

--push .

Benefits:

• Support M1/M2 Macs and Linux x86 in one step

• Avoid runtime architecture bugs


12.11 Use Case: Replacing VMs for Local Dev

Instead of spinning up:

• Ubuntu VM

• Apache + PHP

• MySQL

Use a single container:

docker run -it -p 80:80 php:8.2-apache

Faster than Vagrant or VirtualBox. No virtualization overhead.

12.12 Use Case: E2E Automation Testing with Containers

• Run test server in Docker

• Launch headless browser in separate container

• Use a shared Docker network

Example:

docker network create testnet

docker run -d --network testnet --name web myweb:latest

docker run --rm --network testnet cypress/included

12.13 Use Case: Running Security Tools via Containers

Tools like:

• Trivy

• Nmap

• Nikto

• OWASP ZAP

• SSLyze

Can all be run as containers:

docker run --rm aquasec/trivy image myapp

docker run --rm instrumentisto/nmap [Link]

No need to install them locally.


12.14 Use Case: Building ML & Data Pipelines

Data scientists often use:

• Python

• Jupyter

• Pandas, NumPy

• Custom dependencies

Docker allows shipping the entire stack:

FROM jupyter/scipy-notebook

RUN pip install seaborn nltk scikit-learn

docker run -p 8888:8888 my-ml-env


Module 13: Docker Troubleshooting Playbook
13.1 Why Docker Breaks (And Often)

Containers are designed to be isolated and ephemeral, but real-world factors such as:

• Misconfigured mounts

• Crashes due to memory limits

• Image build failures

• Permission mismatches

• Host networking issues

• Secrets leakage

...can cause unexpected and frustrating issues.

This module helps you systematically diagnose and resolve these container nightmares.

13.2 Core Troubleshooting Workflow

When a Docker-related problem occurs:

1. Check container status

docker ps -a

2. Get logs

docker logs <container>

3. Inspect container details

docker inspect <container>

4. Try interactive shell

docker exec -it <container> sh

5. Check host resources

free -m && df -h && top

13.3 Common Issue: Container Exits Immediately

Diagnosis:

docker ps -a

Check STATUS:

Exited (0) 3 seconds ago


Root Causes:

• No foreground process (e.g., CMD ends immediately)

• Entrypoint misconfigured

• Shell scripts without tail -f or sleep

Fix:

CMD ["tail", "-f", "/dev/null"]

Or fix ENTRYPOINT/CMD to run long-lived service (e.g., web server).

13.4 Issue: Container Keeps Restarting

Look for restart loop:

docker ps --filter "status=restarting"

Root Causes:

• Crash in app process

• Port already in use

• Secrets/configs missing

• Memory OOM crash

Fix:

• Add logging (docker logs -f)

• Remove bad restart policy during testing:

docker run --restart=on-failure ...

13.5 Issue: Image Build Fails (docker build)

Sample Error:

Step 5/9 : RUN npm install

---> Running in ...

npm ERR! no such file or directory

Fix Steps:

1. Use .dockerignore to reduce context

2. Split build steps for faster debug

3. Use docker build --progress=plain


4. Check for missing COPY, broken dependencies

13.6 Issue: Disk Space Full

Check usage:

docker system df

df -h

Fix:

docker system prune -a

docker volume prune

docker image prune

Be careful — pruning may remove unused volumes/images still needed in CI/CD.

13.7 Issue: Permission Denied

Common Errors:

Permission denied

chown: invalid user

Root Causes:

• Host directory mounted with wrong UID

• Read-only bind mounts

• Non-root user in container can’t write

Fix:

docker run -v $(pwd):/data --user $(id -u):$(id -g) myimage

Or adjust ownership inside container:

RUN chown -R appuser:appuser /app

13.8 Issue: Port Already in Use

Error:

bind: address already in use

Fix:
Find the process:

lsof -i :3000

Kill it or map to another port:

docker run -p 3001:3000 myapp

13.9 Issue: Container Logs Missing or Empty

Check logs:

docker logs <container>

Causes:

• App doesn't log to stdout/stderr

• Logs written to file inside container

• Silent process crash

Fix:

Update app to log to stdout:

[Link]("App started")

Or redirect logs in Dockerfile:

CMD ["sh", "-c", "myapp >> /dev/stdout"]

13.10 Issue: OOMKilled (Out of Memory)

Diagnose:

docker inspect <container> | grep -i oom

Check container logs:

docker logs <container>

Fix:

• Increase container memory limit:

docker run --memory=1g ...

• Optimize app memory usage (e.g., avoid memory leaks)


13.11 Issue: File Not Found in Container

Error:

/bin/sh: [Link]: not found

Fix:

• Check COPY path casing and location

• Use docker exec to inspect file paths

• Ensure working directory is set correctly:

WORKDIR /app

13.12 Issue: Bind Mount Doesn’t Reflect Changes

Causes:

• Caching (Docker Desktop)

• VSCode interfering with bind mount

• Incorrect path mapping

Fix:

• Use absolute paths

• Restart Docker Desktop

• Check if file is being cached:

docker run -v $PWD:/app ...

13.13 Issue: Containers Can’t Talk to Each Other

Causes:

• Not on the same user-defined network

• Using IPs instead of service names

• Firewall rules or host DNS settings

Fix:

• Use a custom bridge network:

docker network create mynet

docker run --network mynet ...

• Communicate via container name (db:5432)


13.14 Useful Debugging Tools in Docker

Tool Purpose

docker exec Get a shell in running container

docker logs Review container logs

docker inspect Detailed metadata

nsenter Attach to container namespaces

strace Trace syscalls

lsof, top Runtime diagnostics

13.15 Custom Troubleshooting Container

Use busybox or alpine as debug containers:

docker run -it --network mynet busybox sh

• Ping other containers

• wget, nslookup, ps, etc.


Module 14: Docker Best Practices & Anti-Patterns
14.1 Why Best Practices Matter

As teams grow and environments scale:

• Docker misuse leads to massive image bloat

• Dockerfiles become non-reproducible

• Containers become insecure and fragile

• CI/CD pipelines slow down

This module summarizes field-tested strategies and dangerous anti-patterns to help you build
Docker the right way — clean, secure, optimized.

14.2 Dockerfile Design Best Practices

1. Always Pin Image Versions

# Good

FROM node:20-alpine

# Bad

FROM node:latest

Unpinned versions change over time, causing non-reproducible builds.

2. Use Multi-Stage Builds

FROM golang:1.21 AS builder

WORKDIR /src

COPY . .

RUN go build -o app

FROM scratch

COPY --from=builder /src/app /app

ENTRYPOINT ["/app"]

Keeps final images:

• Small
• Secure (no compilers or package managers)

• Faster to pull/deploy

3. Reduce Layers

# Combine into one RUN layer

RUN apt update && apt install -y curl && rm -rf /var/lib/apt/lists/*

• Minimizes final image size

• Improves cache usage

4. Use .dockerignore

node_modules

.git

coverage/

Prevents sending unnecessary files to Docker daemon during build — speeds up builds and reduces
image context size.

5. Avoid Installing Debug Tools in Final Image

• Use multi-stage: build/debug in stage 1, copy output only to final

• No need for curl, vim, or bash in production images

6. Use Non-Root Users

RUN adduser -D appuser

USER appuser

• Prevents privilege escalation

• Required by some PaaS platforms (e.g., Cloud Run)

7. Use Minimal Base Images

Use Case Image

General alpine, debian:slim

Static binary scratch


Use Case Image

[Link] node:20-alpine

Java eclipse-temurin:<version>-jre

8. Use ENTRYPOINT for Executable

ENTRYPOINT ["python"]

CMD ["[Link]"]

• Allows passing extra args to container (docker run myapp --debug)

• CMD can be overridden easily; ENTRYPOINT sets default command

14.3 Anti-Patterns to Avoid

1. Using ADD Instead of COPY

ADD . /app # BAD (unpacks archives, fetches URLs)

COPY . /app # Safe and predictable

Only use ADD when:

• You need to auto-extract .[Link]

• You want to pull from remote URL (not recommended)

2. Hardcoding Secrets

ENV DB_PASSWORD=secret # Do NOT bake secrets into image

Instead, inject via environment variable or secret volume at runtime.

3. Running as Root

USER root # Exposes container if compromised

Always drop privileges using USER directive.

4. Using latest Tag in Production

• Unpredictable builds

• Might break silently if base image updates

Always pin to specific tag or digest:


FROM python@sha256:<digest>

5. Too Many Layers

RUN apt update

RUN apt install -y curl

RUN echo "done" # Creates 3 layers

Combine related steps:

RUN apt update && apt install -y curl && echo "done"

6. Huge Final Image Sizes

Causes:

• Copying entire repo (COPY . .)

• Not cleaning package caches

• Leaving unused files in image

Use docker image ls and docker history to identify bloat.

14.4 Build Optimization Patterns

• Use --target to build only part of multi-stage image

• Use --platform to prebuild for M1/ARM:

docker buildx build --platform linux/amd64 .

• Run builds with DOCKER_BUILDKIT=1 for faster builds

14.5 Image Size Auditing

docker image ls

docker history <image>

Use tools:

• Dive: analyze image layer sizes

• Trivy: scan for vulnerabilities and misconfigurations


14.6 Security Best Practices

• Sign images using Cosign

• Scan regularly with Trivy

• Use distroless base if possible

• Run containers with:

--cap-drop=ALL --read-only --no-new-privileges

14.7 Operational Best Practices

• Use health checks in Dockerfile:

HEALTHCHECK CMD curl -f [Link] || exit 1

• Use volumes for data, not COPY:

docker run -v data:/app/data myapp

• Use tagging convention for versions:


devopsshack/app:1.0.0, app:1.0.0-rc1, etc.
Module 15: Ultimate Docker Use Case Recap & Production Checklist

15.1 Final Recap of Docker's Capabilities

Docker is the glue of DevOps and modern software delivery pipelines. It enables:

Capability Description

Packaging Bundle code + dependencies as portable images

Rapid Deployment Fast container startups, no need for full OS boot

Testing Environments Run tests in disposable, identical containers

Microservices Isolation Service-level containerization for better separation and scaling

CI/CD Integration Build, scan, test, and deploy from containers

Cloud Compatibility Works on EKS, ECS, GKE, Cloud Run, Azure App Services, etc.

Developer Onboarding Use DevContainers or Compose to standardize dev envs

Security & Observability Integrate with Trivy, Cosign, Prometheus, Falco, etc.

15.2 Final Docker Production Checklist

Security

• Use USER non-root inside Dockerfile

• Drop Linux capabilities with --cap-drop=ALL

• Set --read-only filesystem for runtime

• Use HEALTHCHECK directive

• Set no-new-privileges: true in container config

• Enable image signing (e.g. cosign sign)

• Run Trivy scans pre-deployment

• Never bake secrets into the image (ENV, COPY, ARG)

• Use Kubernetes Secrets or Vault to inject credentials at runtime

Image & Build Hygiene

• Pin base image tags (e.g. FROM node:20-alpine)


• Use .dockerignore to eliminate unwanted files

• Use multi-stage builds to strip build-only dependencies

• Avoid bloated tools in final stage (vim, curl, git)

• Keep layers minimal and combined where appropriate

• Use distroless or alpine wherever possible

Deployment Readiness

• Set proper ENTRYPOINT + CMD

• Map ports explicitly (EXPOSE 8080)

• Externalize configuration using ENV or --env-file

• Mount volumes for persistent data, not COPY

• Use tagging conventions (v1.2.3, release-2025-05)

• Push to trusted registry (Docker Hub, ECR, Harbor)

Observability & Monitoring

• Expose /metrics for Prometheus (where applicable)

• Add logs to stdout/stderr (avoid file-based logs)

• Instrument readiness + liveness probes (if used with K8s)

• Use sidecars (e.g. Fluent Bit, Filebeat) if needed

15.3 Cheat Sheet – Docker CLI Essentials

# Build & run

docker build -t myapp .

docker run -d -p 8080:80 myapp

# Logs & inspect

docker logs -f <container>

docker inspect <container>

docker exec -it <container> sh

# Networking
docker network create mynet

docker run --network mynet ...

# Volumes

docker volume create pgdata

docker run -v pgdata:/var/lib/postgresql/data ...

# Cleanup

docker system prune -a

docker image prune

docker volume prune

15.4 Template: Secure Production Dockerfile (Node Example)

# Stage 1 - Build

FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

# Stage 2 - Run

FROM node:20-alpine

RUN adduser -D appuser

USER appuser

WORKDIR /app

COPY --from=builder /app/dist ./

CMD ["node", "[Link]"]


15.5 Template: Runtime Flags for Hardened Containers

docker run \

--read-only \

--cap-drop=ALL \

--security-opt no-new-privileges \

--memory=512m \

--pids-limit=100 \

--user 1000:1000 \

-v /tmp:/tmp \

-p 8080:8080 \

myapp

15.6 Template: Docker Compose with Secrets, Volumes, Limits

version: "3.9"

services:

web:

image: devopsshack/app:1.0.0

ports:

- "80:80"

environment:

DB_HOST: db

secrets:

- db_password

deploy:

resources:

limits:

memory: 512M

cpus: "0.5"

db:

image: postgres:15

volumes:
- pgdata:/var/lib/postgresql/data

environment:

POSTGRES_PASSWORD_FILE: /run/secrets/db_password

volumes:

pgdata:

secrets:

db_password:

file: ./secrets/db_password.txt

15.7 Real-World Docker Toolchain Stack (2025+ Ready)

Area Tool

Image Build Docker BuildKit, Kaniko, Buildah

Registry Docker Hub, AWS ECR, Harbor

Scanning Trivy, Grype, Snyk

Signing Cosign, Notary v2

CI/CD GitHub Actions, GitLab, ArgoCD

Secrets HashiCorp Vault, Doppler, SOPS

Tests Testcontainers, Playwright, Cypress

Deploy Kubernetes, ECS, GKE, Cloud Run

Monitoring Prometheus, Grafana, Loki

Runtime Sec Falco, AppArmor, Seccomp, gVisor

15.8 Final Tips Before You Docker in Production

1. Always scan before you ship


Don’t deploy an image without a vulnerability scan.

2. Log everything to stdout/stderr


Avoid writing to log files inside container.

3. Never bake secrets into Dockerfiles


Secrets should be runtime-injected only.
4. Small images = fast deploys
Use alpine, slim, or distroless.

5. Make your Dockerfile reproducible


Pin everything. No latest.

6. Push only signed images


Use cosign sign --key ... before pushing to registry.

7. Always tag and version


Never just push :latest unless you want to live dangerously.

You might also like