Dockerfiles

Learn how Dockerfiles work, how Docker images are built, and why image building is foundational in modern container infrastructure.

So far we have mostly used existing images:

docker run nginx
docker run postgres

But eventually you will want to containerize your own applications.

For example:

  • Node.js APIs
  • Python applications
  • Go services
  • frontend applications
  • backend systems

This is where Dockerfiles become essential.

A Dockerfile defines:

how Docker should build an image

Dockerfiles are one of the most important concepts in the entire Docker ecosystem.

Modern container infrastructure depends heavily on automated image building.


What is a Dockerfile?

A Dockerfile is a text file containing instructions for building Docker images.

Simplified workflow:

Dockerfile
docker build
Docker Image
Docker Container

Instead of manually configuring environments:

Define Build Process Declaratively

This creates reproducible images.


Basic Dockerfile Example

Example:

FROM nginx
COPY . /usr/share/nginx/html

Build image:

docker build -t my-nginx .

Simplified process:

Read Dockerfile
Execute Instructions
Build Image Layers

Why Dockerfiles Matter

Before containers, deployments often involved:

  • manual installation
  • dependency setup
  • environment configuration
  • troubleshooting inconsistent servers

Dockerfiles changed this dramatically.

Now infrastructure can be defined as code.

Example:

Application Setup
Written Inside Dockerfile

This improves:

  • reproducibility
  • automation
  • portability
  • deployment consistency

Dockerfile Instructions

Dockerfiles use instructions.

Common examples:

FROM
COPY
RUN
CMD
ENV
WORKDIR
EXPOSE

Each instruction creates image layers.

Understanding this is extremely important.


The FROM Instruction

Every Dockerfile usually starts with:

FROM

Example:

FROM ubuntu

This defines the base image.

Simplified model:

Base Image
Add Application Layers

The base image provides the initial filesystem and environment.


The COPY Instruction

Example:

COPY . /app

Simplified behavior:

Host Files
Copied Into Image

This usually copies application code into the container image.


The RUN Instruction

Example:

RUN npm install

This executes commands during image building.

Simplified process:

Temporary Build Container
Execute Command
Save Result As Layer

RUN commonly installs:

  • packages
  • dependencies
  • tools

The CMD Instruction

Example:

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

This defines the default startup command.

Simplified behavior:

Container Starts
Run Default Process

Remember:

containers live as long as the main process lives

CMD commonly defines that process.


The WORKDIR Instruction

Example:

WORKDIR /app

This changes the working directory inside the image.

Equivalent idea:

cd /app

for later instructions.

Very common in application images.


The ENV Instruction

Example:

ENV NODE_ENV=production

Defines default environment variables inside the image.

Applications can read these values during runtime.


The EXPOSE Instruction

Example:

EXPOSE 3000

Important:

EXPOSE does NOT publish ports

It only documents intended ports.

Actual external access still requires:

-p

during container startup.


Building Images

To build images:

docker build -t my-app .

Simplified meaning:

Build Context = Current Directory

Docker sends files to the Docker Engine and processes the Dockerfile.


Image Build Layers

Every instruction creates layers.

Example:

FROM ubuntu
RUN apt install nodejs
COPY . /app

Simplified layer structure:

Layer 1 → Ubuntu
Layer 2 → Node.js Installed
Layer 3 → Application Files

Docker caches layers aggressively.

This improves build performance significantly.


Docker Build Cache

Example:

COPY package.json .
RUN npm install
COPY . .

Why separate copies?

Because:

Dependency Layer Can Be Cached

If only application code changes:

npm install does not rerun

Efficient layer ordering is a major optimization skill.


.dockerignore

Docker sends files into the build context.

Without filtering:

node_modules
.git
logs
temporary files

may be included unnecessarily.

Example .dockerignore:

node_modules
.git

This improves:

  • build speed
  • image size
  • security

Real-World Node.js Example

Example:

FROM node:22

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 3000

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

Simplified workflow:

Base Node.js Image
Install Dependencies
Copy Application
Start Server

This is the foundation of many real deployments.


Multi-Stage Builds

Modern Dockerfiles often use:

multi-stage builds

Example concept:

Build Stage
Compile Application
Copy Final Artifacts
Minimal Runtime Image

This dramatically reduces final image size.

Extremely important in production environments.


Why Small Images Matter

Smaller images improve:

  • deployment speed
  • scaling speed
  • storage efficiency
  • security

Example:

Large Image
Slow Deployments

vs:

Small Image
Fast Deployments

Image optimization becomes critical at scale.


Common Beginner Mistake

One common beginner mistake:

Treat Dockerfiles Like VM Setup Scripts

Containers should remain:

  • minimal
  • reproducible
  • focused on one application

Large monolithic container images create maintenance problems.


Infrastructure Thinking

Dockerfiles introduced a major infrastructure shift:

Application Environment

became:

Versioned Build Artifact

This transformed software delivery pipelines.

Modern CI/CD systems heavily rely on automated image builds.


Dockerfiles and CI/CD

Typical modern workflow:

Git Push
CI Pipeline
Build Docker Image
Push To Registry
Deploy Containers

Dockerfiles became foundational for automated deployments.


Why This Matters

Understanding Dockerfiles is critical before learning:

  • CI/CD pipelines
  • image optimization
  • registries
  • Kubernetes deployments
  • production infrastructure

Dockerfiles are one of the core foundations of modern cloud-native engineering.


Key Takeaways

  • Dockerfiles define how images are built
  • Images are built layer by layer
  • Docker caches layers for efficiency
  • FROM defines the base image
  • RUN executes build commands
  • CMD defines the default startup process
  • .dockerignore improves builds and security
  • Multi-stage builds reduce image size
  • Dockerfiles enable reproducible infrastructure
  • Modern CI/CD systems heavily rely on Docker image building