Dockerfiles
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
FROMdefines the base imageRUNexecutes build commandsCMDdefines the default startup process.dockerignoreimproves builds and security- Multi-stage builds reduce image size
- Dockerfiles enable reproducible infrastructure
- Modern CI/CD systems heavily rely on Docker image building