Docker & Container Tools Mastery
In this tutorial, you'll learn about Docker & Container Tools Mastery. We cover key concepts, practical examples, and best practices.
Docker standardizes application deployment by packaging code and dependencies into lightweight, portable containers that run consistently across any environment.
What You'll Learn
In this tutorial, you'll learn Dockerfile best practices, multi-stage builds for smaller images, docker-compose for multi-service applications, Docker networking and volumes, private registries, container security scanning, and integration with CI/CD pipelines.
Why It Matters
Containerization eliminates the "it works on my machine" problem. Teams deploy faster, scale horizontally, and run the same image in development, staging, and production. Docker images also reduce supply chain risk by pinning exact dependency versions.
Real-World Use
DodaZIP's backend runs as a set of Docker containers: Nginx reverse proxy, Node.js API server, Redis cache, and PostgreSQL database. Docker Compose defines the stack locally; Kubernetes orchestrates it in production.
flowchart LR A[Dockerfile] --> B[Build Image] B --> C[Registry] C --> D[Pull] D --> E[Container Runtime] E --> F[Single Container] E --> G[Docker Compose] E --> H[Kubernetes] G --> I[Multi-service App] H --> J[Production Cluster]
Dockerfile Best Practices
Efficient Dockerfile
# Use specific base image tags (never "latest")
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files first for layer caching
COPY package*.json ./
RUN npm ci --only=production
# Copy source code
COPY . .
RUN npm run build
# Multi-stage: runtime image is smaller
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]
Expected behavior: The first stage (builder) has all build tools. The final image contains only the compiled output and production dependencies — typically 70-80% smaller than a single-stage build.
.dockerignore
node_modules
.git
*.md
.env
dist/
coverage/
.DS_Store
Expected behavior: Files listed in .dockerignore are excluded from the Docker build context. This speeds up builds and prevents secrets from leaking into the image.
Docker Compose for Multi-Service Apps
# docker-compose.yml
version: "3.9"
services:
api:
build: ./api
ports:
- "3000:3000"
environment:
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
Expected behavior: <a href="/devops/docker-compose/">docker compose</a> up -d starts all three services. The API server connects to db and redis using their service names as hostnames. Data persists in the pgdata volume across restarts.
Docker Networking
# Create a custom bridge network
docker network create --driver bridge app-network
# Run containers on the same network
docker run -d --name api --network app-network my-api:latest
docker run -d --name db --network app-network postgres:16-alpine
# Containers resolve each other by name
docker exec api ping db
Expected behavior: Containers on the same user-defined bridge network can communicate using container names as hostnames. No need for --link or hardcoded IPs.
Container Tool Comparison
| Tool | Purpose | Configuration | Orchestration |
|---|---|---|---|
| Docker | Container runtime | Dockerfile | Docker Compose |
| Docker Compose | Multi-service apps | YAML | Single host |
| Podman | Daemonless alternative | Docker-compatible | Pods, Compose |
| Kubernetes | Production cluster | YAML manifests | Multi-host |
| Docker Swarm | Simple clustering | Compose-compatible | Multi-host |
Container Security
Image Scanning
# Scan for vulnerabilities in local images
docker scout quick my-api:latest
# Check a specific image from a registry
docker scout registry my-registry.io/my-api:latest
Expected output: A vulnerability report listing CVEs by severity (CRITICAL, HIGH, MEDIUM, LOW) with package names, affected versions, and fix versions.
Running as Non-Root
# Always create and use a non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
Expected behavior: If the container is compromised, the attacker has limited privileges. Running as root inside a container is a common security anti-pattern.
Read-Only Root Filesystem
docker run --read-only --tmpfs /tmp my-api:latest
Expected behavior: The container's filesystem is read-only. Only /tmp (mounted as tmpfs) can be written to. This prevents malware from modifying system files inside the container.
Common Errors
- Using
latesttag in production —latestis a floating tag that changes unpredictably. Pin exact versions:node:20.15.0-alpine. - Storing secrets in Dockerfiles — Environment variables in Dockerfiles are visible in image history. Use Docker secrets or a
.envfile (and add it to.dockerignorefor build args). - Not cleaning up unused resources —
docker system prune -ashould be run periodically to remove dangling images, stopped containers, and unused networks. - Ignoring layer caching — Copy source code only after installing dependencies. Otherwise, every code change triggers a full dependency reinstall.
- Exposing unnecessary ports — Only expose ports your service actually uses. Avoid exposing internal ports like database ports (5432, 3306) to the host.
Practice Questions
What is the difference between
COPYandADDin Dockerfile?COPYcopies files from context to image.ADDcan also fetch remote URLs and auto-extract archives. UseCOPYunless you needADD's specific features.How do you persist database data across container restarts? Use a named volume (
docker volume create pgdata) and mount it to the database's data directory indocker-compose.yml.What happens when you run
<a href="/devops/docker-compose/">docker compose</a> up --scale api=3? Docker Compose starts 3 replicas of the API service behind the same network. Each replica gets a unique container name.How do you debug a container that exits immediately? Remove
--detach(-d) and run interactively, or check logs withdocker logs <container-id>. Usedocker run -it --entrypoint sh <image>to start a shell instead of the default command.
Challenge
Create a Docker Compose setup for a full-stack application with a React frontend (nginx to serve static files), a Node.js API server, a PostgreSQL database, and a Redis cache. Include health checks, volume mounts, environment variables, and a network configuration.
Mini Project: Containerize a File Scanning Service
Build and containerize a simple file scanning service:
- Write a Node.js or Python script that scans files for known malware signatures (hash comparison)
- Create a multi-stage Dockerfile that keeps the final image under 150MB
- Use docker-compose to run the scanner alongside a Redis queue for job distribution
- Add a health check endpoint (
/health) that the orchestrator can poll - Configure the container to run as non-root with a read-only filesystem
- Use
docker scoutto scan the final image and fix any CRITICAL vulnerabilities
This mirrors how Durga Antivirus Pro's cloud scanning service is deployed.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro