Feedback

Chat Icon

Painless Docker - 2nd Edition

A Comprehensive Guide to Mastering Docker and its Ecosystem

Dockerfile: Instructions, Best Practices, and Gotchas
31%

Dockerfile Instructions

FROM

In a Dockerfile, the foundational step begins with the FROM instruction, specifying the base image you want to use.

If you're aiming to create a multi-stage build, where one image is used as a base for another, you can incorporate multiple FROM instructions within a single Dockerfile.

FROM :

Example:

FROM ubuntu:24.04

If you omit the tag, Docker defaults to fetching the image tagged latest.

MAINTAINER

The MAINTAINER line in a Dockerfile isn't a functional instruction in the same sense as others. Instead, it provides metadata about the individual or organization responsible for the image. It can include the name, email, or even a website.

MAINTAINER 

Example:

MAINTAINER Aymen EL Amri - @eon01

RUN

In a Dockerfile, the RUN instruction allows you to execute commands, just as you would on the command line. There are two syntax forms for the RUN instruction: the shell form and the exec form.

Shell Form: RUNparam1 param2 ... paramN

For Linux-based containers, this command runs with the /bin/sh -c shell, and for Windows, it uses the cmd /S /C shell.

Example:

RUN ls -l

When building the Docker image, you'll see output similar to this:

Step 4 : RUN ls -l
 ---> Running in b3e87d26c09a
total 64
... [trimmed output]
drwxr-xr-x  13 root root 4096 Oct 13 21:13 var
drwxr-xr-x   2 root root 4096 Oct 13 21:13 sbin
drwxr-xr-x   2 root root 4096 Oct 13 21:13 media
... [trimmed output]

Exec Form: RUN ["executable", "param1", "param2", ..., "paramN"]

In this form, the command and its parameters are specified as a JSON array. This approach is particularly useful when you want to avoid shell string munging or when you need to run commands that include special characters.

Example:

RUN ["/bin/sh", "-c", "ls", "-l"]

The exec form is generally preferred for its clarity and precision.

LABEL

The LABEL instruction allows you to attach metadata to your Docker image. This metadata is represented as key-value pairs. It's beneficial for storing additional information about the image, such as version numbers, contact details, or licensing information.

Syntax:

LABEL key1=value1 key2=value2 ... keyN=valueN

Example:

LABEL version="1.0" maintainer="John Doe "

It's important to note that not only Docker images can utilize labels. In the Docker ecosystem, labels can be attached to:

  • Docker Containers: Running instances of a Docker image
  • Docker Daemons: The background service running on the host that manages building, running, and managing Docker containers
  • Docker Volumes: The storage volumes associated with containers
  • Docker Networks: Networks that connect containers, aiding in communication
  • Docker Swarm Nodes: Individual machines, VMs, or physical computers that are members of a Swarm
  • Docker Swarm Services: A Swarm-specific term representing a group of tasks (or containers) to ensure designated workloads run in a specified state

Using labels, you can organize, manage, and track resources better in a Docker environment. For example, you can filter containers based on labels when using commands like docker ps --filter "label=key=value" or docker images --filter "label=key=value".

EXPOSE

The role of the EXPOSE instruction is purely descriptive. It indicates which network ports the container will listen on when it's running. This instruction doesn't actually publish the ports; it simply serves as documentation for users and tools.

For example, if your application inside the container listens on port 8080, you would include the following line in your Dockerfile:

EXPOSE 8080

If it listens on multiple ports, you can specify them all:

EXPOSE 8080 9090

When there's a range of ports to expose, you can define it like this:

# expose ports from 8000 to 8010
EXPOSE 8000-8010

If you need to specify a protocol (TCP or UDP), you can do so by appending it to the port number:

EXPOSE 8080/tcp 9090/udp

ENV

The ENV instruction in a Dockerfile is used to set environment variables. It's akin to the export command in Linux. It uses a / pair format. You can set environment variables in two ways:

One variable per line:

ENV variable1 value1
ENV variable2 value2

Multiple variables in a single line:

ENV variable1="value1" variable2="value2"

When the application runs inside the container, it can access these environment variables. For example, if you set an environment variable named APP_ENV to production, your application and any scripts running inside the container can retrieve this value.

These are some examples of how to access environment variables in different programming languages and shell scripts once they're set using ENV:

Example with PHP:


  $env = getenv('APP_ENV');
  echo "The application is running in the $env environment.";
?>

Example with Python:

import os
env = os.getenv('APP_ENV')
print(f"The application is running in the {env} environment.")

Example with Node.js:

const env = process.env.APP_ENV;
console.log(`The application is running in the ${env} environment.`);

Example with Bash:

#!/bin/bash
echo "The application is running in the $APP_ENV environment."

ARG

The ARG instruction is very similar to ENV, but it's not used to export a variable to the container's runtime environment. Instead, ARG is used to define variables that can be passed at build time to the Docker build process (on your local machine).

The general syntax for ARG is:

ARG [=]

Here is an example of defining a build-time variable with a default value:

ARG APP_VERSION=1.0

Variables declared with ARG can be overridden during the build process. You can do this using the docker build command and the --build-arg flag:

docker build --build-arg APP_VERSION=2.0 -t my-app .

A common use case for ARG when working with Debian or Ubuntu-based images is to set the DEBIAN_FRONTEND variable to noninteractive. Interactive prompts during package installations can cause builds to hang, so setting this variable helps avoid that issue.

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y mysql-server

In this case, mysql-server will be installed without prompting for user input (e.g., setting a root password).

WORKDIR

The WORKDIR instruction is used to set the working directory for any subsequent commands in the Dockerfile. This is especially handy for streamlining and simplifying the paths used in subsequent instructions.

Here's the general way to use the WORKDIR instruction:

WORKDIR 

Consider a scenario where you want to copy an index.html file into the /var/www directory. Instead of specifying the full path in the ADD instruction like:

ADD index.html /var/www

You can set the working directory first using WORKDIR and then simplify the ADD command:

WORKDIR /var/www
ADD index.html .

If the directory specified in the WORKDIR instruction doesn't already exist, Docker will create it for you.

ADD

The ADD instruction is primarily used to copy files or directories from the host system to the container's filesystem. It offers some added functionality beyond the capabilities of the COPY instruction, such as remote file retrieval and on-the-fly tar extraction.

The ADD instruction can be written in two forms:

The most common form:

ADD  

If your path contains spaces, use the following format:

ADD ["", ""]

The ADD instruction supports wildcard characters like * and ?:

  • * matches all files in a directory.
# Copy all files from /var/www/ to /var/www/
ADD /var/www/* /var/www/
  • ? matches any single character.
# Copy files named index.html and index.htm from /var/www/ to /var/www/
ADD /var/www/index.htm? /var/www/

You can also use ADD to directly fetch files from remote URLs:

ADD https://github.com/twbs/bootstrap/raw/main/dist/js/bootstrap.js \
  /var/www/static/js/

ADD is intuitive when it comes to compressed files. If a recognized archive format (e.g., gzip, bzip2, or xz) is provided as a source, Docker will automatically unpack it into the specified directory:

ADD source_data.tar.gz /dest/

For the above command, the source_data.tar.gz archive is extracted to /dest/ inside the image (assuming it's a valid tarball).

COPY

The COPY instruction is an essential tool in Dockerfiles, allowing users to copy files or directories from a source on the host to the container's filesystem. While similar to the ADD instruction, COPY is simpler and doesn't handle archive extraction or URL fetching.

The COPY instruction can be written in two forms, like ADD, and it also supports wildcard characters (e.g., * and ?).

Here is a basic example:

COPY ["/home/eon01/painlessdocker/index.html", "/var/www/"]

ENTRYPOINT

The ENTRYPOINT instruction in Docker answers the question: "What should happen when a container starts?" It specifies a command that will always be executed when the container is launched (the PID 1 process).

Suppose you want to launch a Python or Node.js server. You could use commands like:

python -m SimpleHTTPServer

or

node app.js

Without a defined ENTRYPOINT, the container might start and then immediately stop because it lacks an ongoing task.

There are also two forms of the ENTRYPOINT instruction:

Exec form (recommended):

ENTRYPOINT ["", "", "", ... ""]

Shell form:

ENTRYPOINT    ... 

Example:

ENTRYPOINT ["node", "app.js"]

Consider a simple Python script that prints "Hello World":

print("Hello World")

Painless Docker - 2nd Edition

A Comprehensive Guide to Mastering Docker and its Ecosystem

Enroll now to unlock all content and receive all future updates for free.

Unlock now  $31.99$25.59

Hurry! This limited time offer ends in:

To redeem this offer, copy the coupon code below and apply it at checkout:

Learn More