Building Docker Images with Dockerfiles

Building Docker Images with Dockerfiles

A Dockerfile serves as a text document containing all the commands required to assemble a Docker image. The docker build command processes these instructions to create an executable image. You can specify a Dockerfile from any location in your filesystem using the -f flag with the docker build command.

Basic Structure of a Dockerfile

A Dockerfile typically consists of four main sections: base image specification, maintainer information, image build instructions, and container startup commands. Comments in Dockerfiles begin with the # character.

Dockerfile Commands

Docker processes instructions in a Dockerfile sequentially from top to bottom. The first instruction must be FROM to specify the base image. Various instructions like RUN, CMD, FROM, EXPOSE, and ENV can be used to construct the image.

FROM: Specifying the Base Image

The FROM instruction initializes a new build stage and sets the base image for subsequent instructions. It must be the first non-comment instruction in the Dockerfile.

FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>

Example:

FROM ubuntu:20.04

Note: If no tag or digest is specified, the latest tag is used by default.

MAINTAINER: Setting Author Information

The MAINTAINER instruction allows you to set the author field of the generated images.

MAINTAINER <name>

Examples:

MAINTAINER Jane Smith
MAINTAINER john.doe@example.com
MAINTAINER Jane Smith <john.doe@example.com>

RUN: Executing Commands During Build

The RUN instruction executes any command in a new layer on top of the current image and commits the results.

Two forms are available:

# Shell form
RUN <command>

# Exec form
RUN ["executable", "param1", "param2"]

Examples:

RUN apt-get update && apt-get install -y python3
RUN ["/bin/bash", "-c", "echo hello world"]

Note: Docker caches intermediate layers created by RUN instructions. To disable caching, use the --no-cache flag with docker build.

ADD: Copying Files and URLs

The ADD instruction copies new files, directories, or remote file URLs from <src> and adds them to the filesystem of the container at the path <dest>.

ADD <src>... <dest>
ADD ["<src>",... "<dest>"]  # For paths containing spaces

Examples:

ADD hom* /mydir/          # Add all files starting with "hom"
ADD hom?.txt /mydir/      # Add files like "home.txt" (? matches single character)
ADD test relativeDir/     # Add "test" to WORKDIR/relativeDir/
ADD http://example.com/file /data/  # Download and add a file

Note: ADD automatically extracts compressed files (except remote URLs) from the local build context.

COPY: Copying Files

The COPY instruction copies new files or directories from <src> and adds them to the filesystem of the container at the path <dest>. Similar to ADD, but with less functionality.

COPY <src>... <dest>
COPY ["<src>",... "<dest>"]  # For paths containing spaces

Examples:

COPY src/ /usr/src/app/
COPY ["config file", /etc/config/]

CMD: Setting Default Commands

The CMD instruction sets a default command that will be executed only when you run the container without specifying a command.

Three forms are available:

# Exec form (preferred)
CMD ["executable", "param1", "param2"]

# Providing default parameters for ENTRYPOINT
CMD ["param1", "param2"]

# Shell form
CMD command param1 param2

Examples:

CMD ["nginx", "-g", "daemon off;"]
CMD echo "This is a test."
CMD ["/usr/bin/wc", "--help"]

Note: CMD is different from RUN - CMD specifies the command to run when starting a container, while RUN specifies commands during image building. Only the last CMD instruction in a Dockerfile will be used.

ENTRYPOINT: Configuring Default Executable

The ENTRYPOINT instruction allows you to configure a container that will run as an executable.

Two forms are available:

# Exec form (preferred)
ENTRYPOINT ["executable", "param1", "param2"]

# Shell form
ENTRYPOINT command param1 param2

Examples:

ENTRYPOINT ["top", "-b"]
ENTRYPOINT /bin/sh -c "echo hello"

Note: ENTRYPOINT is similar to CMD but differs in that command line arguments to docker run will be appended to the ENTRYPOINT, not override it. Only the last ENTRYPOINT instruction in a Dockerfile will be used.

Combining CMD and ENTRYPOINT

When used together, CMD provides default parameters for the ENTRYPOINT instruction:

ENTRYPOINT ["executable", "param1"]
CMD ["param2"]

Running docker run image will execute: executable param1 param2

Running docker run image custom will execute: executable param1 custom

LABEL: Adding Metadata

The LABEL instruction adds metadata to an image in the form of key-value pairs.

LABEL <key>=<value> <key>=<value> ...

Examples:

LABEL version="1.0" description="Web server" vendor="Example Corp"
LABEL maintainer="John Doe <john.doe@example.com>"

Note: Using a single LABEL instruction with multiple labels is recommended to reduce the number of intermediate layers.

ENV: Setting Environment Variables

The ENV instruction sets environment variables in the container.

Two forms are available:

ENV <key> <value>  # Only sets one variable
ENV <key>=<value> ...  # Sets multiple variables

Examples:

ENV APP_VERSION=1.3.7
ENV NODE_ENV=production
ENV DEBIAN_FRONTEND=noninteractive

EXPOSE: Declaring Ports

The EXPOSE instruction informs Docker that the container listens on specified network ports at runtime.

EXPOSE <port> [<port>/<protocol>...]

Examples:

EXPOSE 80
EXPOSE 80/tcp 443/tcp
EXPOSE 53/udp

Note: EXPOSE doesn't actually publish the port. It's documentation for the person running the container. To publish the port, use the -p flag with docker run or the -P flag to publish all exposed ports.

VOLUME: Creating Mount Points

The VOLUME instruction creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containers.

VOLUME ["/path/to/dir"]

Examples:

VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2"]

Features of volumes:

  • Can be shared and reused between containers
  • Changes to volumes are immediate
  • Changes to volumes don't affect the base image
  • Exist until no containers use them

WORKDIR: Setting Working Directory

The WORKDIR instruction sets the working directory for any subsequent instructions like RUN, CMD, ENTRYPOINT, COPY, and ADD.

WORKDIR /path/to/workdir

Examples:

WORKDIR /app
WORKDIR /app/backend
WORKDIR /app/backend/api

Note: If the WORKDIR doesn't exist, it will be created automatically. The working directory can be overridden with the -w flag when running a container.

USER: Setting User

The USER instruction sets the user name or UID to use when running the image and for any subsequent RUN instructions.

USER <username>[:<group>]
USER <UID>[:<GID>]

Examples:

USER www-data
USER user1:group1
USER 1001

Note: The user must be created in the image with the RUN instruction or in the base image first. The user can be overridden with the -u flag when running a container.

ARG: Defining Build Variables

The ARG instruction defines a variable that users can pass at build time to the builder using the --build-arg flag.

ARG <name>[=<default value>]

Examples:

ARG BUILD_DATE
ARG VERSION=1.0
ARG GIT_COMMIT=unknown

ONBUILD: Setting Build Triggers

The ONBUILD instruction adds a trigger instruction to the image that will be executed later when the image is used as a base for another build.

ONBUILD [INSTRUCTION]

Examples:

ONBUILD ADD . /app/src
ONBUILD RUN npm install
ONBUILD RUN chown -R www-data:www-data /app

Example Dockerfile

Here's an example Dockerfile for creating a Python application:

# Python application Dockerfile
# Version 2.0

# Base image
FROM python:3.9-slim

# Set maintainer
LABEL maintainer="Jane Doe <jane.doe@example.com>"

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Create app directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Create non-root user
RUN useradd --create-home --shell /bin/bash appuser
USER appuser

# Expose port
EXPOSE 8000

# Start command
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:application"]

Understanding CMD and ENTRYPOINT Interaction

Both CMD and ENTRYPOINT define what command gets executed when running a container. However, they work differently together:

  • CMD: Provides default arguments for the executing command. These can be overridden from the command line when running the container.
  • ENTRYPOINT: Configures a container that will run as an executable. Arguments from the command line are appended to the ENTRYPOINT command.

When used together, CMD provides default arguments to ENTRYPOINT:

ENTRYPOINT ["executable", "param1"]
CMD ["param2"]

Behavior:

  • docker run image: executes executable param1 param2
  • docker run image custom: executes executable param1 custom
  • docker run image --flag: executes executable param1 --flag

This pattern allows you to create a container that always runs a specific executable (via ENTRYPOINT) while allowing users to customize its behavior via command line arguments (which get passed to CMD).

Tags: docker dockerfile containerization devops image-building

Posted on Fri, 17 Jul 2026 17:27:25 +0000 by Naez