Dockerfile: building images for containers
Author: Domeny.ForSale editorial team Published
In brief: A Dockerfile is a text file containing instructions that Docker uses to build an application image—a package containing the application’s code, required tools and settings. You can use this image to run containers: isolated processes with a prepared environment. A Dockerfile therefore describes how to prepare an application to run, but is not itself a running container.
Dockerfile, image and container—three different things
Think of a Dockerfile as a recipe, an image as the package prepared from that recipe, and a container as a running instance of that package. One image can be used to create multiple containers, such as several instances of the same application.
In a Dockerfile, you specify the base environment, files to copy, dependencies to install and default startup command, among other things. This removes the need to prepare each server manually by following separate instructions. After changing the code or dependencies, you usually build a new image.
A container is not a full virtual machine: it uses the operating system kernel of the environment where the container engine runs. An image helps standardize the application’s environment, but does not eliminate every difference between computers. Processor architecture, network configuration and file access still matter, for example.
What instructions does a Dockerfile contain?
The file is usually named Dockerfile, with no extension. Each instruction describes a specific build step or image setting. The most common are:
FROM—selects a base image, such as a Python or Node.js environment.WORKDIR—sets the working directory for subsequent instructions and the application when it runs.COPY—copies files from the build context into the image.RUN—executes a command during the build, such as installing libraries.ENV—sets environment variables.EXPOSE—declares the port used by the application; it does not automatically make that port accessible outside the container.CMD—specifies the default command executed when the container starts.
The distinction between RUN and CMD is essential. RUN executes during image creation, whereas CMD runs only when the container starts. Dependency installation usually belongs in RUN, and server startup in CMD.
There is also ENTRYPOINT, which specifies the container’s main program. You can combine it with CMD to supply default arguments to that program. For a first simple example, CMD alone is enough.
A simple example: an HTML page in a container
Suppose you want to run a simple page locally without installing Python on your computer. You need a working Docker installation and a directory containing two files: index.html and Dockerfile.
Put the following in index.html:
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Docker example</title>
<h1>The page is running in a container</h1>
</html>
The Dockerfile file could look like this:
FROM python:3.13-slim
WORKDIR /app
COPY index.html .
EXPOSE 8000
CMD ["python", "-m", "http.server", "8000", "--bind", "0.0.0.0"]
Docker will use an image containing Python, set /app as the working directory and copy the page into it. When the container starts, it will launch a simple HTTP server. The address 0.0.0.0 means the server listens on all of the container’s network interfaces, allowing you to reach it through the published port.
In a terminal, from the directory containing these files, run:
docker build -t my-page .
docker run --rm -p 127.0.0.1:8080:8000 my-page
The first command builds the image and names it my-page. The dot specifies the current directory as the build context: the set of files available to the build process. The second command starts a container and maps local port 8080 to container port 8000.
Open http://localhost:8080 in your browser. Binding the port to 127.0.0.1 restricts access to your local computer. Stop the server with Ctrl+C; the --rm option removes the container after it stops. This is a learning example, not a production server configuration.
What affects build speed and security?
Docker can reuse the results of earlier steps through its cache. Changing files used by an instruction may require that step and subsequent dependent steps to run again. That is why, for applications with library dependencies, it is common to copy the dependency file and install packages first, then copy the code that changes more frequently.
The .dockerignore file excludes unnecessary files from the build context. These typically include the .git directory, local environments, logs and files containing secrets. This reduces both the amount of data transferred and the risk of accidentally including confidential material.
Do not store passwords or tokens in a Dockerfile or copy them into the image. Deleting a secret in a later step does not guarantee its removal from earlier image layers. Supply secrets needed during the build through BuildKit’s secret mechanisms, and supply data needed by the running application through appropriate runtime environment configuration.
For production use, consider running the application as a user without administrator privileges. Also keep track of the base image version: a tag may point to a changed image over time, whereas a digest identifies specific content. Check the chosen image’s official documentation for available versions and update procedures.
When do you need a Dockerfile, and when do you need Compose?
A Dockerfile answers the question: “How do I build the image?” Docker Compose lets you define and run a set of services, such as an application, a database and a cache, along with their networks, ports and volumes. A volume is a mechanism for storing data independently of an individual container’s lifetime.
These tools can work together: Compose can build an image from a Dockerfile, then run it alongside a ready-made database image. If you use only ready-made images, you do not always need your own Dockerfile.
Start by building and running one simple image. Then replace the example page with your own application, explicitly define its dependencies and check which files actually belong in the image.
See also: What is an API?
