Skip to content

Docker & Container Tools Mastery

DodaTech Updated 2026-06-22 5 min read

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

  1. Using latest tag in productionlatest is a floating tag that changes unpredictably. Pin exact versions: node:20.15.0-alpine.
  2. Storing secrets in Dockerfiles — Environment variables in Dockerfiles are visible in image history. Use Docker secrets or a .env file (and add it to .dockerignore for build args).
  3. Not cleaning up unused resourcesdocker system prune -a should be run periodically to remove dangling images, stopped containers, and unused networks.
  4. Ignoring layer caching — Copy source code only after installing dependencies. Otherwise, every code change triggers a full dependency reinstall.
  5. Exposing unnecessary ports — Only expose ports your service actually uses. Avoid exposing internal ports like database ports (5432, 3306) to the host.

Practice Questions

  1. What is the difference between COPY and ADD in Dockerfile? COPY copies files from context to image. ADD can also fetch remote URLs and auto-extract archives. Use COPY unless you need ADD's specific features.

  2. 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 in docker-compose.yml.

  3. 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.

  4. How do you debug a container that exits immediately? Remove --detach (-d) and run interactively, or check logs with docker logs <container-id>. Use docker 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:

  1. Write a Node.js or Python script that scans files for known malware signatures (hash comparison)
  2. Create a multi-stage Dockerfile that keeps the final image under 150MB
  3. Use docker-compose to run the scanner alongside a Redis queue for job distribution
  4. Add a health check endpoint (/health) that the orchestrator can poll
  5. Configure the container to run as non-root with a read-only filesystem
  6. Use docker scout to 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