DevOpsLesson
DevOpsLesson

Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.

Stay Updated

Get notified about new tutorials and features.

Tutorials

  • What is DevOps?
  • Docker Tutorial
  • Terraform Tutorial
  • CI/CD Pipeline
  • All Tutorials

Roadmaps

  • DevOps Engineer
  • Cloud Engineer
  • SRE Path
  • All Roadmaps

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 DevOpsLesson. All rights reserved.

DOCKERKUBERNETESTERRAFORMAWSCI/CDLINUXGITDEVOPS ROADMAPCLOUD ROADMAPSRE ROADMAPGIT CHEATSHEETDOCKER CHEATSHEETK8S CHEATSHEETTF CHEATSHEETLINUX CHEATSHEETDOCKERFILE LINTERYAML VALIDATORCRON PARSERREGEX TESTER

Docker Tutorial

Introduction to docker
Why Use Docker?
Docker vs Virtual Machines
Installing Docker
Key Docker Concepts
Docker Images
Docker Containers
Writing Dockerfiles
Docker Volumes

Writing Dockerfiles

PreviousPrev
Next

Learn to write Dockerfiles from scratch - every instruction explained, with patterns and pitfalls from real-world usage.

Writing Dockerfiles

A Dockerfile is the source of truth for your container image. It's a plain text file - a sequence of instructions that Docker executes top-to-bottom to produce a repeatable, portable image. Master it, and you can package any application to run anywhere.

Why Dockerfiles Matter

Before Dockerfiles, deploying software meant documenting a maze of manual steps: install this library, edit that config file, set these environment variables. Dockerfiles replace all of that with executable documentation - a script that is also the deployment artifact.

# This 8-line file replaces a 20-page deployment guide
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]

Every Instruction at a Glance

InstructionPurpose
FROMSet the base image
WORKDIRSet the working directory
COPYCopy files from host into the image
ADDLike COPY, with URL and archive support
RUNExecute a command at build time
ENVSet environment variables (build + runtime)
ARGSet build-time-only variables
EXPOSEDocument which ports the app uses
USERSwitch to a non-root user
VOLUMEDeclare a mount point
LABELAdd metadata key-value pairs
CMDDefault command when container starts
ENTRYPOINTFixed executable for the container
HEALTHCHECKDefine a health check command
ONBUILDTrigger instructions for child images

A Complete Annotated Example

# ── Base ─────────────────────────────────────────────────────
FROM node:20-alpine
# Use a specific, small base image. Alpine is ~5MB vs ~1GB for node:latest.

# ── Metadata ─────────────────────────────────────────────────
LABEL maintainer="team@example.com"
LABEL version="1.0.0"
LABEL description="My production web API"

# ── Working Directory ─────────────────────────────────────────
WORKDIR /app
# All subsequent instructions run relative to /app.

# ── Dependencies (cached layer) ──────────────────────────────
COPY package*.json ./
RUN npm ci --omit=dev
# Copy manifest first → install → then copy source.
# This way npm ci only reruns when package.json changes.

# ── Application Code ──────────────────────────────────────────
COPY --chown=node:node . .
# Copy source last (changes most often).
# --chown ensures the app user owns the files.

# ── Runtime Config ────────────────────────────────────────────
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000

# ── Security ──────────────────────────────────────────────────
USER node
# Never run as root in production.

# ── Startup ───────────────────────────────────────────────────
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "server.js"]

How the Build Works

Docker Build: Layer Caching Flow

Dockerfile instruction
docker build reads instruction
Already cached?
Yes
Reuse layer
(fast)
No
Execute instruction
Create new layer
Cache the layer

Put instructions that change rarely near the top — they stay cached longer

Each instruction that changes the filesystem creates an immutable layer. Layers are stacked to form the final image. This is why instruction order matters so much for build speed.

What's Covered in This Section

SubsectionWhat You'll Learn
FROMChoosing base images, variants, multi-stage FROM, scratch
COPY & ADDCopying files, ownership, .dockerignore, when to use ADD
RUNBuild-time commands, layer efficiency, shell vs exec form
CMD & ENTRYPOINTDefault commands, exec vs shell form, combining both
ENV & ARGRuntime vs build-time variables, defaults, security
WORKDIR & EXPOSEWorking directories, port documentation
Best PracticesSecurity, caching, size, linting, non-root users
Real-World ExamplesComplete Dockerfiles for Node, Python, Go, and React apps

Your First Dockerfile

Try building a minimal image right now:

Build from a Dockerfile

Concept

The current directory contains a simple Dockerfile. Build it and tag the image as 'myapp:v1'.

▸ your answerPress Run to check
answer

The .dockerignore File

Before Docker sends your files to the build daemon, it filters them through .dockerignore. This is always the first thing to set up:

# .dockerignore
node_modules/
.git/
.env
.env.*
*.log
coverage/
dist/
.DS_Store
.vscode/
README.md
tests/

Without it, COPY . . might copy gigabytes of node_modules or accidentally bake secrets into your image. With it, the build context shrinks to just what the Dockerfile actually needs.

Key Mental Model

Think of a Dockerfile as two distinct phases:

Build time - Everything that runs during docker build:

  • FROM, WORKDIR, COPY, ADD, RUN, ARG, LABEL, USER, EXPOSE, VOLUME

Runtime - Everything that activates when a container starts:

  • ENV, CMD, ENTRYPOINT, HEALTHCHECK

RUN is build-time. CMD is runtime. Confusing these two is the most common beginner mistake.

RUN npm run build      # ✓ Runs ONCE during build, output baked into image
CMD ["npm", "start"]   # ✓ Runs EVERY TIME a container starts
PreviousPrev
Next

Continue Learning

Volumes & Storage

Persist data beyond container lifecycles with named volumes and bind mounts. Understand when to use each and how to manage them.

9 min·Easy

Env Vars & Config

Pass configuration into containers safely using environment variables, env files, and Docker secrets - without baking secrets into your images.

7 min·Easy

Cleanup & Pruning

Keep your Docker environment clean. Remove stopped containers, reclaim disk space, and build automated cleanup habits.

6 min·Easy

Explore Related Topics

Ku

Kubernetes Tutorials

Orchestrate your Docker containers at scale

CI

CI/CD Tutorials

Automate Docker builds and deployments in pipelines

Try the Tool

Dockerfile Linter

Instantly lint your Dockerfile for best-practice violations and security issues.

On This Page

Writing DockerfilesWhy Dockerfiles MatterEvery Instruction at a GlanceA Complete Annotated ExampleHow the Build WorksWhat's Covered in This SectionYour First DockerfileThe `.dockerignore` FileKey Mental Model