Docker Curriculum
Dockerfile Overview
A Dockerfile is a plain text file that contains all the instructions needed to build a Docker image. It is the blueprint — you write it once, and Docker uses it to reproduce the exact same image every time it is built, on any machine, in any environment.
Mastering Dockerfiles is one of the most important skills you can develop as a DevOps engineer. Nearly every application you deploy to AWS ECS, EKS, or Fargate starts with a Dockerfile. The quality of your Dockerfile directly affects your image size, build speed, security posture, and application reliability.
What a Dockerfile Does
A Dockerfile describes a series of steps to build an image. Docker reads the file top to bottom, executes each instruction, and creates a layer for each instruction that modifies the filesystem. The final stack of layers is your image.
A Dockerfile answers the questions:
- What base environment should this image start from?
- What files need to be added to the image?
- What commands need to run to set up the application?
- What should happen when a container starts from this image?
Your First Dockerfile
Here is a minimal but complete Dockerfile for a Python Flask web application:
Build this into an image:
Run a container from it:
That is the complete cycle — write the Dockerfile, build the image, run the container.
The Dockerfile Format
Every line in a Dockerfile is either:
- An instruction — a keyword in uppercase followed by its arguments
- A comment — a line starting with
# - A blank line — ignored by Docker
Instructions are case-insensitive — FROM and from both work. However, the convention is to write instructions in uppercase so they stand out clearly from the arguments.
The Most Important Dockerfile Instructions
Here is a quick overview of every instruction you will use. Each one gets a deep explanation in the next file.
FROM
Every Dockerfile must start with FROM. It sets the base image — the starting point for your build.
WORKDIR
Sets the working directory for all subsequent instructions. If the directory does not exist, Docker creates it.
COPY
Copies files from the build context (your local machine) into the image.
RUN
Executes a command during the build. The result becomes a new image layer.
ENV
Sets environment variables that are available at build time and at runtime.
ARG
Defines build-time variables — passed with --build-arg during docker build. Not available at runtime.
EXPOSE
Documents the port the container listens on. Does not actually open or publish the port.
CMD
Sets the default command that runs when the container starts. Can be overridden by the user.
ENTRYPOINT
Sets the main executable that always runs when the container starts. CMD provides default arguments to it.
USER
Sets the user for subsequent instructions and the final container process. Critical for security — do not run as root.
LABEL
Adds metadata key-value pairs to the image.
VOLUME
Declares a mount point for external volumes. Signals that this path should be managed as a volume.
ADD
Like COPY but also supports URLs and automatically extracts tar archives.
In most cases, prefer COPY over ADD — it is more explicit and predictable. Use ADD only when you need the automatic extraction feature.
HEALTHCHECK
Defines a command Docker uses to check if the container is healthy.
The .dockerignore File
Before Docker sends your build context to the daemon, it filters out files listed in .dockerignore. This is placed in the same directory as your Dockerfile.
Without .dockerignore:
- Your
.gitdirectory is sent to the daemon (slow, large) - Your
.envfile with secrets is sent (security risk — secrets can end up in the image) - All your test files are included in the image (larger image)
With .dockerignore, only what the build actually needs is sent. This makes builds faster and images smaller.
Dockerfile Location and Naming
By default, Docker looks for a file named Dockerfile in the build context directory:
You can specify a different filename with -f:
Common naming patterns:
A Complete Real-World Example
Here is a production-quality Dockerfile for a Node.js API:
This Dockerfile demonstrates:
- Specific version pinning (
node:20-alpine) - Security updates applied in base
- Non-root user created and used
- Dependency file copied before app code (cache optimization)
- Production-only dependencies (
npm ci --only=production) - Health check configured
- Port documented
- Minimal and clean structure
Summary
- A Dockerfile is a plain text file with instructions that Docker reads top to bottom to build an image
- Every filesystem-modifying instruction creates a new image layer
- Key instructions:
FROM,WORKDIR,COPY,RUN,ENV,EXPOSE,CMD,ENTRYPOINT,USER,LABEL,HEALTHCHECK - Always create a
.dockerignorefile to exclude unnecessary files from the build context — prevents secrets from leaking into images and speeds up builds - Use specific image version tags in
FROMinstructions — neverFROM python:latestin production - A well-written Dockerfile produces a small, fast-building, secure image that behaves identically everywhere it runs
In the next file, we go deep into every Dockerfile instruction — exactly how each one works, the correct syntax, and the common mistakes to avoid.