Setting up a containerized application's dependencies and configurations can be complex and time-consuming. However, Docker provides a way to automate this setup using a Dockerfile.
This article explains what a Dockerfile is and shows how to create one.

Prerequisites
- Docker installed (read How to Install Docker on Ubuntu and Installing Docker on CentOS and Rocky Linux).
- Command-line access.
What Is Dockerfile?
A Dockerfile is a text document with instructions telling Docker how to assemble an image layer by layer. It allows the user to define:
- The base operating system for the container (e.g., Ubuntu, Rocky Linux, Alpine). ย
- The software packages and dependencies.
- The included application code and files.
- The environment variables.
- The commands to be executed when the container starts.
- Other configuration details for the containerized environment.
Dockerfiles automate the image creation process, eliminating manual setup and configuration. They guarantee consistent image builds across different environments and ensure portability.
Dockerfile Syntax
Dockerfile consists of a series of instructions, each on a new line. An instruction is a keyword (like FROM
, RUN
, COPY
, etc.) that tells Docker what action to perform.
Docker processes the instructions sequentially from top to bottom, and each instruction builds upon the state of the image resulting from the previous instruction. Therefore, the order of instructions in a Dockerfile is crucial.
Below is the basic syntax for instructions:
INSTRUCTION [argument]
Note: Instructions are case-insensitive, but are typically written in uppercase to make them easily distinguishable.
Arguments provide the details or parameters for the instruction. The argument format argument depends on the specific instruction. Complex instructions can use the backslash character (\) to continue the instruction on the next line. For example:
RUN apt update && \
apt install -y --no-install-recommends \
package-1 \
package-2
Dockerfile Instructions
Below is a table outlining the most common Dockerfile instructions. Syntax examples are provided for each.
Instruction | Description | Example |
---|---|---|
FROM | Sets the base image for subsequent instructions. | FROM python:3.9-slim-buster |
RUN | Executes a command in a new layer on top of the current image and commits the results. Used to install software, run scripts, and set up an application environment. | RUN apt update && apt install -y --no-install-recommends \<br>python3-pip \<br>nginx |
COPY | Copies new files or directories from a source path on the host machine to a destination path in the image's filesystem. Often used to import application code and configuration files into the container. | COPY config.ini /etc/app/ |
ADD | Works like COPY (see ADD vs. COPY) but with additional features, such as extracting tar archives and fetching files from URLs. | ADD mydata.tar.gz /data |
WORKDIR | Keeps Dockerfile organized by setting the working directory for any subsequent RUN , CMD , ENTRYPOINT , COPY , and ADD instructions. | WORKDIR /app |
ENV | Sets environment variables inside the container. The application can access these variables at runtime. | ENV PYTHON_VERSION 3.9 |
CMD | Provides default commands to execute when the container starts. There can only be one CMD instruction in a Dockerfile. Running docker run with a command overrides the instruction. | CMD ["nginx", "-g", "daemon off;"] |
ENTRYPOINT | Defines the primary executable for the container. When the user runs the container, Docker executes the command specified in ENTRYPOINT . Arguments passed to docker run are appended to the ENTRYPOINT command. | ENTRYPOINT ["/bin/sh", "-c"] |
VOLUME | Creates a mount point with the specified name and marks it as holding externally mounted volumes from the native host or other containers. | VOLUME ["/var/log/app", "/etc/config"] |
HEALTHCHECK | Tells Docker how to test a container to ensure it is still working. The user can specify a command to check the container's health. | HEALTHCHECK --interval=5m --timeout=3s \<br>CMD curl -f http://localhost:8080 || exit 1 |
ARG | Defines a build-time variable that users can pass to the builder using the --build-arg = <var>=<value> flag. | ARG NODE_VERSION=16 |
LABEL | Adds metadata to the image in the form of key-value pairs. Useful for organizing and identifying images. | LABEL maintainer="[email protected]" |
Shell Form vs. Exec Form
The RUN
, CMD
, and ENTRYPOINT
instructions can have two forms:
- The shell form introduces an argument as a string that Docker executes using
/bin/sh -c
(or another shell specified by theSHELL
instruction). Below is an example of a set of two instructions written in the shell form:
RUN apt-get update && apt-get install -y example-package
CMD echo "Hello, World!"
- The exec form of an argument is a JSON array where the first element is the executable, and the subsequent elements are the arguments passed to it. For example:
RUN ["apt", "update"]
RUN ["apt", "install", "-y", "example-package"]
CMD ["echo", "Hello, World!"]
While the shell form is convenient for simple commands and shell features like piping, the exec form ensures the predictable execution of complex commands by eliminating shell interference.
How to Create Dockerfile
Generating a Dockerfile involves setting up a project directory, creating a file, and populating it with the necessary instructions. Follow the steps below to create a Dockerfile:
1. Use the mkdir command to create a project directory:
mkdir [directory]
Replace [directory]
with the actual directory name. The directory will contain the Dockerfile and store all other files involved in building the image.
2. Go to the project directory:
cd [directory]
3. Create a file named Dockerfile with a text editor. This article uses Nano:
nano Dockerfile
4. Write the instructions. For example, the following code creates an example Docker image that:
- Uses Ubuntu as a base.
- Runs the apt command to update the repositories.
- Executes an echo command that prints "Hello, World!" in the output:
FROM ubuntu:24.04
LABEL maintainer="[email protected]"
RUN apt update
CMD ["echo", "Hello, World!"]
When you finish adding commands to the Dockerfile, save the file and exit.
Build Image From Dockerfile
The following steps show how to use a Dockerfile to create a Docker image:
1. Execute the docker build
command using the following syntax:
docker build -t <image>:<tag> <path>
The -t
option allows the user to name and tag the new image. If the command is executed from the project directory, use (.
) instead of writing the full path. For example:
docker build -t test-app:latest .
2. List the Docker images on the system to check if the system generated the image successfully:
docker images
The image appears in the list:
3. Test the app with docker run
:
docker run test-app
The example text shows in the output.
Dockerfile Best Practices
Following best practices for creating Dockerfiles ensures efficient, reproducible, and secure Docker image builds. The list below contains the key strategies for optimizing Docker image creation:
- Start with a specific base image. Instead of ubuntu or ubuntu:latest, avoid unexpected changes by specifying a version like ubuntu:22.04 or ubuntu:noble.
- Choose minimal images. Choose smaller base images like Alpine Linux when possible. Smaller images produce faster builds and fewer security vulnerabilities. Using multi-stage builds results in smaller final images as only the necessary artifacts are copied into the final stage.
- Combine
RUN
instructions. EachRUN
instruction creates a new image layer. Combine multiple related commands into a singleRUN
instruction to reduce the number of layers. - Optimize the order of instructions. Docker builds images by caching layers. Place instructions that are less likely to change (like installing base packages) earlier in the Dockerfile. Put instructions that change frequently (like copying application code) later.
- Set default values. Using
ENV
to set environment variables makes configuration easier and can be overridden at runtime. - Monitor application health. Use the
HEALTHCHECK
instruction to define a command Docker can run to check if a container is healthy and functioning correctly. This strategy is crucial for orchestration tools like Docker Compose and Kubernetes. - Run as a non-root user. For security reasons, run application processes as a non-root user inside the container. Create a dedicated user and group and switch to that user using the
USER
instruction.
Conclusion
After reading this article, you should have a solid grasp of Dockerfiles and how to build them. The article introduced important Dockerfile instructions, provided steps for creating images using Dockerfile, and listed some best practices.
Next, read how to optimize Docker performance.