Dockerfile is a declarative text file that defines the steps required to assemble a Docker image. It enables reproducible, version-controlled, and automated image builds—essential for modern containerized application delivery.
Core Build Workflow
- Author a Dockerfile with instructions describing environment setup, dependencies, and runtime configuration.
- Execute
docker buildto process the file and generate an immutable image layer by layer. - Launch containers from the resulting image using
docker run. - Push the finalized image to a registry (e.g., Docker Hub or Alibaba Cloud Container Registry) via
docker push.
Anatomy of a Dockerfile
- Instructions are case-sensitive and must be uppercase (e.g.,
FROM,RUN). - Execution proceeds top-to-bottom; each instruction creates a new filesystem layer.
- Lines starting with
#are comments (unless they begin# syntax=). - Each
RUN,COPY, orADDcommand produces a cached intermediate layer—critical for efficient rebuilds.
Key Instructions Explained
| Instruction | Purpose |
|---|---|
FROM |
Specifies the base image (e.g., centos:7, alpine:latest). Required as the first non-comment line. |
LABEL |
Adds metadata (e.g., maintainer, version, license) in key-value format. Replaces deprecated MAINTAINER. |
ENV |
Sets persistent environment variables accessible during build and at runtime. |
WORKDIR |
Defines the default working directory for subsequent RUN, CMD, ENTRYPOINT, and COPY instructions. |
RUN |
Executes commands in a new shell layer (e.g., installing packages, configuring services). |
COPY |
Copies local files or directories into the image filesystem (more secure and explicit than ADD). |
EXPOSE |
Documents which ports the container listens on (does not publish them; use -p at runtime). |
CMD |
Provides default executable and arguments invoked when the container starts. Overridden by CLI arguments to docker run. Only the last CMD takes effect. |
ENTRYPOINT |
Configures a container to run as an executable. Arguments passed to docker run append to the ENTRYPOINT array, enabling flexible command composition. |
ONBUILD |
Triggers instructions only when the current image serves as a base for another FROM instruction. |
Practical Example: Extending CentOS 7
Official CentOS images are minimal—often lacking common utilities like vim or net-tools. To create a more usable variant:
-
Initialize project structure:
mkdir -p ~/docker-builds/centos-enhanced && cd ~/docker-builds/centos-enhanced -
Create
Dockerfile:FROM centos:7 LABEL maintainer="dev@example.com" ENV APP_HOME=/opt/app WORKDIR $APP_HOME # Fix yum repos for EOL CentOS 7 RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo && \ sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*.repo && \ yum makecache && \ yum -y install vim-enhanced net-tools && \ yum clean all EXPOSE 80 CMD ["/bin/bash"] -
Build the image:
docker build -t enhanced-centos:7.9 .
⚠️ Note on Repository Mirrors: As CentOS Linux 7 reached end-of-life, its official mirrors no longer serve updated packages. The
sedsubstitutions above redirectyumtovault.centos.org, which hosts historical snapshots.
Testing the Resulting Image
# Launch interactively
$ docker run -it enhanced-centos:7.9
# Verify tools exist and work
[root@5a2b1c4d8e9f app]# vim --version
VIM - Vi IMproved 7.4...
[root@5a2b1c4d8e9f app]# ifconfig eth0 | grep "inet "
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
# Confirm WORKDIR is active
[root@5a2b1c4d8e9f app]# pwd
/opt/app
Understanding CMD vs ENTRYPOINT
Scenario A: Using CMD
Dockerfile-cmd:
FROM alpine:3.19
CMD ["echo", "hello"]
Build and test:
docker build -f Dockerfile-cmd -t cmd-demo .
docker run cmd-demo # → "hello"
docker run cmd-demo world # → Error: "world" not found as executable
The CMD is fully replaced by CLI arguments — world attempts to launch a binary named world, failing.
Scenario B: Using ENTRYPOINT
Dockerfile-entrypoint:
FROM alpine:3.19
ENTRYPOINT ["echo", "hello"]
Build and test:
docker build -f Dockerfile-entrypoint -t entry-demo .
docker run entry-demo # → "hello"
docker run entry-demo world # → "hello world"
Here, world appends to the ENTRYPOINT array, forming echo hello world.
When to Choose Which?
- Use
CMDwhen you want users to easily override the default command entirely (e.g., debugging shells, ad-hoc commands). - Use
ENTRYPOINTwhen the container should behave like a binary—accepting flags or subcommands while preserving core functionality (e.g., database clients, CLI tools, or wrapper scripts).
Multiple ENTRYPOINT instructions are invalid; only the final one applies. You may combine both: ENTRYPOINT sets the executable, and CMD provides default arguments that can be overridden.
Inspecting Build History
To audit how layers were created:
docker history enhanced-centos:7.9
This reveals each instruction, layer ID, size, and creation timestamp—valuable for optimization and security analysis.
Final Notes
- Prefer
COPYoverADDunless tar extraction or remote URL fetching is needed. - Chain related
RUNcommands with&&to minimize layers and reduce image size. - Clean package caches (
yum clean all,apt-get clean) in the sameRUNstep where packages are installed. - Use
.dockerignoreto exclude unnecessary files (e.g.,.git,node_modules) from the build context.