Dockerfile: building images for containers

Author: 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:

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?

Read also

Learn how PHP-FPM handles PHP requests, how it differs from a web server, and how to tune processes for traffic and hosting resources.
Learn how BIND, open-source DNS server software, handles domains, how its two roles differ and when running it yourself makes sense.
Learn what Poland’s REGON register and identification number mean, how they differ from NIP and KRS, and how to check a business’s details.