Other Dockerfile Directives
In the section Common Directives in Dockerfile, we discussed the common directives available for a Dockerfile. In that section, we discussed FROM, LABEL, RUN, CMD, and ENTRYPOINT directives and how to use them to create a simple Dockerfile.
In this section, we will be discussing more advanced Dockerfile directives. These directives can be used to create more advanced Docker images. For example, we can use the VOLUME directive to bind the filesystem of the host machine to a Docker container. This will allow us to persist the data generated and used by the Docker container. Another example is the HEALTHCHECK directive, which allows us to define health checks to evaluate the health status of Docker containers. We will look into the following directives in this section:
- The
ENVdirective - The
ARGdirective - The
WORKDIRdirective - The
COPYdirective - The
ADDdirective - The
USERdirective - The
VOLUMEdirective - The
EXPOSEdirective - The
HEALTHCHECKdirective - The
ONBUILDdirective
The ENV Directive
The ENV directive in Dockerfile is used to set environment variables. Environment variables are used by applications and processes to get information about the environment in which a process runs. One example would be the PATH environment variable, which lists the directories to search for executable files.
Environment variables are defined as key-value pairs as per the following format:
ENV <key> <value>
The PATH environment variable is set with the following value:
$PATH:/usr/local/myapp/bin/
Hence, it can be set using the ENV directive as follows:
ENV PATH $PATH:/usr/local/myapp/bin/
We can set multiple environment variables in the same line separated by spaces. However, in this form, the key and value should be separated by the equal to (=) symbol:
ENV <key>=<value> <key>=<value> ...
In the following example, there are two environment variables configured. The PATH environment variable is configured with the value of $PATH:/usr/local/myapp/bin/, and the VERSION environment variable is configured with the value of 1.0.0:
ENV PATH=$PATH:/usr/local/myapp/bin/ VERSION=1.0.0
Once an environment variable is set with the ENV directive in the Dockerfile, this variable is available in all subsequent Docker image layers. This variable is even available in the Docker containers launched from this Docker image.
In the next section, we will look into the ARG directive.
The ARG Directive
The ARG directive is used to define variables that the user can pass at build time. ARG is the only directive that can precede the FROM directive in the Dockerfile.
Users can pass values using --build-arg <varname>=<value>, as shown here, while building the Docker image:
$ docker image build -t <image>:<tag> --build-arg <varname>=<value> .
The ARG directive has the following format:
ARG <varname>
There can be multiple ARG directives in a Dockerfile, as follows:
ARG USER ARG VERSION
The ARG directive can also have an optional default value defined. This default value will be used if no value is passed at build time:
ARG USER=TestUser ARG VERSION=1.0.0
Unlike the ENV variables, ARG variables are not accessible from the running container. They are only available during the build process.
In the next exercise, we will use the knowledge gained so far to use ENV and ARG directives in a Dockerfile.
Exercise 2.03: Using ENV and ARG Directives in a Dockerfile
Your manager has asked you to create a Dockerfile that will use ubuntu as the parent image, but you should be able to change the ubuntu version at build time. You will also need to specify the publisher's name and application directory as the environment variables of the Docker image. You will use the ENV and ARG directives in the Dockerfile to perform this exercise:
- Create a new directory named
env-arg-exerciseusing themkdircommand:mkdir env-arg-exercise
- Navigate to the newly created
env-arg-exercisedirectory using thecdcommand:cd env-arg-exercise
- Within the
env-arg-exercisedirectory, create a file namedDockerfile:touch Dockerfile
- Now, open the
Dockerfileusing your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile. Then, save and exit from theDockerfile:# ENV and ARG example ARG TAG=latest FROM ubuntu:$TAG LABEL maintainer=sathsara@mydomain.com ENV PUBLISHER=packt APP_DIR=/usr/local/app/bin CMD ["env"]
This
Dockerfilefirst defined an argument namedTAGwith the default value of the latest. The next line is theFROMdirective, which will use the ubuntu parent image with theTAGvariable value sent with thebuildcommand (or the default value if no value is sent with the build command). Then, theLABELdirective sets the value for the maintainer. Next is theENVdirective, which defines the environment variable ofPUBLISHERwith the valuepackt, andAPP_DIRwith the value of/usr/local/app/bin. Finally, use theCMDdirective to execute theenvcommand, which will print all the environment variables. - Now, build the Docker image:
$ docker image build -t env-arg --build-arg TAG=19.04 .
Note the
env-arg --build-arg TAG=19.04flag used to send theTAGargument to the build process. The output should be as follows:
Figure 2.5: Building the env-arg Docker image
Note that the
19.04tag of the ubuntu image was used as the parent image. This is because you sent the--build-arg flagwith the value ofTAG=19.04during the build process. - Now, execute the
docker container runcommand to start a new container from the Docker image that you built in the last step:$ docker container run env-arg
As we can see from the output, the
PUBLISHERenvironment variable is available with the value ofpackt, and theAPP_DIRenvironment variable is available with the value of/usr/local/app/bin:
Figure 2.6: Running the env-arg Docker container
In this exercise, we defined environment variables for a Docker image using the ENV directive. We also experienced how to use ARG directives to pass values during the Docker image build time. In the next section, we will be covering the WORKDIR directive, which can be used to define the current working directory of the Docker container.
The WORKDIR Directive
The WORKDIR directive is used to specify the current working directory of the Docker container. Any subsequent ADD, CMD, COPY, ENTRYPOINT, and RUN directives will be executed in this directory. The WORKDIR directive has the following format:
WORKDIR /path/to/workdir
If the specified directory does not exist, Docker will create this directory and make it the current working directory, which means this directive executes both mkdir and cd commands implicitly.
There can be multiple WORKDIR directives in the Dockerfile. If a relative path is provided in a subsequent WORKDIR directive, that will be relative to the working directory set by the previous WORKDIR directive:
WORKDIR /one WORKDIR two WORKDIR three RUN pwd
In the preceding example, we are using the pwd command at the end of the Dockerfile to print the current working directory. The output of the pwd command will be /one/two/three.
In the next section, we will discuss the COPY directive that is used to copy files from the local filesystem to the Docker image filesystem.
The COPY Directive
During the Docker image build process, we may need to copy files from our local filesystem to the Docker image filesystem. These files can be source code files (for example, JavaScript files), configuration files (for example, properties files), or artifacts (for example, JAR files). The COPY directive can be used to copy files and folders from the local filesystem to the Docker image during the build process. This directive takes two arguments. The first one is the source path from the local filesystem, and the second one is the destination path on the image filesystem:
COPY <source> <destination>
In the following example, we are using the COPY directive to copy the index.html file from the local filesystem to the /var/www/html/ directory of the Docker image:
COPY index.html /var/www/html/index.html
Wildcards can also be specified to copy all files that match the given pattern. The following example will copy all files with the .html extension from the current directory to the /var/www/html/ directory of the Docker image:
COPY *.html /var/www/html/
In addition to copying files, the --chown flag can be used with the COPY directive to specify the user and group ownership of the files:
COPY --chown=myuser:mygroup *.html /var/www/html/
In the preceding example, in addition to copying all the HTML files from the current directory to the /var/www/html/ directory, the --chown flag is used to set file ownership, with the user as myuser and group as mygroup:
Note
The --chown flag is only supported from Docker version 17.09 and above. For Docker versions below 17.09, you need to run the chown command after the COPY command to change file ownership.
In the next section, we will look at the ADD directive.
The ADD Directive
The ADD directive is also similar to the COPY directive, and has the following format:
ADD <source> <destination>
However, in addition to the functionality provided by the COPY directive, the ADD directive also allows us to use a URL as the <source> parameter:
ADD http://sample.com/test.txt /tmp/test.txt
In the preceding example, the ADD directive will download the test.txt file from http://sample.com and copy the file to the /tmp directory of the Docker image filesystem.
Another feature of the ADD directive is automatically extracting the compressed files. If we add a compressed file (gzip, bzip2, tar, and so on) to the <source> parameter, the ADD directive will extract the archive and copy the content to the image filesystem.
Imagine we have a compressed file named html.tar.gz that contains index.html and contact.html files. The following command will extract the html.tar.gz file and copy the index.html and contact.html files to the /var/www/html directory:
ADD html.tar.gz /var/www/html
Since the COPY and ADD directives provide almost the same functionality, it is recommended to always use the COPY directive unless you need the additional functionality (add from a URL or extract a compressed file) provided by the ADD directive. This is because the ADD directive provides additional functionality that can behave unpredictably if used incorrectly (for example, copying files when you want to extract, or extracting files when you want to copy).
In the next exercise, we are going to use the WORKDIR, COPY, and ADD directives to copy files into the Docker image.
Exercise 2.04: Using the WORKDIR, COPY, and ADD Directives in the Dockerfile
In this exercise, you will deploy your custom HTML file to the Apache web server. You will use Ubuntu as the base image and install Apache on top of it. Then, you will copy your custom index.html file to the Docker image and download the Docker logo (from the https://www.docker.com website) to be used with the custom index.html file:
- Create a new directory named
workdir-copy-add-exerciseusing themkdircommand:mkdir workdir-copy-add-exercise
- Navigate to the newly created
workdir-copy-add-exercisedirectory:cd workdir-copy-add-exercise
- Within the
workdir-copy-add-exercisedirectory, create a file namedindex.html. This file will be copied to the Docker image during build time:touch index.html
- Now, open
index.htmlusing your favorite text editor:vim index.html
- Add the following content to the
index.htmlfile, save it, and exit fromindex.html:<html> <body> <h1>Welcome to The Docker Workshop</h1> <img src="logo.png" height="350" width="500"/> </body> </html>
This HTML file will output
Welcome to The Docker Workshopas the header of the page andlogo.png(which we will download during the Docker image build process) as an image. You have defined the size of thelogo.pngimage as a height of350and a width of500. - Within the
workdir-copy-add-exercisedirectory, create a file namedDockerfile:touch Dockerfile
- Now, open the
Dockerfileusing your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile, save it, and exit from theDockerfile:# WORKDIR, COPY and ADD example FROM ubuntu:latest RUN apt-get update && apt-get install apache2 -y WORKDIR /var/www/html/ COPY index.html . ADD https://www.docker.com/sites/default/files/d8/2019-07/ Moby-logo.png ./logo.png CMD ["ls"]
This
Dockerfilefirst defines the ubuntu image as the parent image. The next line is theRUNdirective, which will executeapt-get updateto update the package list, andapt-get install apache2 -yto install the Apache HTTP server. Then, you will set/var/www/html/as the working directory. Next, copy theindex.htmlfile that we created in step 3 to the Docker image. Then, use theADDdirective to download the Docker logo from https://www.docker.com/sites/default/files/d8/2019-07/Moby-logo.png to the Docker image. The final step is to use thelscommand to print the content of the/var/www/html/directory. - Now, build the Docker image with the tag of
workdir-copy-add:$ docker image build -t workdir-copy-add .
You will observe that the image is successfully built and tagged as
latestsince we did not explicitly tag our image:
Figure 2.7: Building the Docker image using WORKDIR, COPY, and ADD directives
- Execute the
docker container runcommand to start a new container from the Docker image that you built in the previous step:$ docker container run workdir-copy-add
As we can see from the output, both the
index.htmlandlogo.pngfiles are available in the/var/www/html/directory:index.html logo.png
In this exercise, we observed how the WORKDIR, ADD, and COPY directives work with Docker. In the next section, we are going to discuss the USER directive.
The USER Directive
Docker will use the root user as the default user of a Docker container. We can use the USER directive to change this default behavior and specify a non-root user as the default user of a Docker container. This is a great way to improve security by running the Docker container as a non-privileged user. The username specified with the USER directive will be used to run all subsequent RUN, CMD, and ENTRYPOINT directives in the Dockerfile.
The USER directive takes the following format:
USER <user>
In addition to the username, we can also specify the optional group name to run the Docker container:
USER <user>:<group>
We need to make sure that the <user> and <group> values are valid user and group names. Otherwise, the Docker daemon will throw an error while trying to run the container:
docker: Error response from daemon: unable to find user my_user: no matching entries in passwd file.
Now, let's try our hands at using the USER directive in the next exercise.
Exercise 2.05: Using USER Directive in the Dockerfile
Your manager has asked you to create a Docker image to run the Apache web server. He has specifically requested that you use a non-root user while running the Docker container due to security reasons. In this exercise, you will use the USER directive in the Dockerfile to set the default user. You will be installing the Apache web server and changing the user to www-data. Finally, you will execute the whoami command to verify the current user by printing the username:
Note
The www-data user is the default user for the Apache web server on Ubuntu.
- Create a new directory named
user-exercisefor this exercise:mkdir user-exercise
- Navigate to the newly created
user-exercisedirectory:cd user-exercise
- Within the
user-exercisedirectory, create a file namedDockerfile:touch Dockerfile
- Now, open the
Dockerfileusing your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile, save it, and exit from theDockerfile:# USER example FROM ubuntu RUN apt-get update && apt-get install apache2 -y USER www-data CMD ["whoami"]
This
Dockerfilefirst defines the Ubuntu image as the parent image. The next line is theRUNdirective, which will executeapt-get updateto update the package list, andapt-get install apache2 -yto install the Apache HTTP server. Next, you use theUSERdirective to change the current user to thewww-datauser. Finally, you have theCMDdirective, which executes thewhoamicommand, which will print the username of the current user. - Build the Docker image:
$ docker image build -t user .
The output should be as follows:

Figure 2.8: Building the user Docker image
- Now, execute the
docker containerrun command to start a new container from the Docker image that we built in the previous step:$ docker container run user
As you can see from the following output,
www-datais the current user associated with the Docker container:www-data
In this exercise, we implemented the USER directive in the Dockerfile to set the www-data user as the default user of the Docker image.
In the next section, we will discuss the VOLUME directive.
The VOLUME Directive
In Docker, the data (for example, files, executables) generated and used by Docker containers will be stored within the container filesystem. When we delete the container, all the data will be lost. To overcome this issue, Docker came up with the concept of volumes. Volumes are used to persist the data and share the data between containers. We can use the VOLUME directive within the Dockerfile to create Docker volumes. Once a VOLUME is created in the Docker container, a mapping directory will be created in the underlying host machine. All file changes to the volume mount of the Docker container will be copied to the mapped directory of the host machine.
The VOLUME directive generally takes a JSON array as the parameter:
VOLUME ["/path/to/volume"]
Or, we can specify a plain string with multiple paths:
VOLUME /path/to/volume1 /path/to/volume2
We can use the docker container inspect <container> command to view the volumes available in a container. The output JSON of the docker container inspect command will print the volume information similar to the following:
"Mounts": [
{
"Type": "volume",
"Name": "77db32d66407a554bd0dbdf3950671b658b6233c509ea
ed9f5c2a589fea268fe",
"Source": "/var/lib/docker/volumes/77db32d66407a554bd0
dbdf3950671b658b6233c509eaed9f5c2a589fea268fe/_data",
"Destination": "/path/to/volume",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
As per the preceding output, there is a unique name given to the volume by Docker. Also, the source and destination paths of the volume are mentioned in the output.
Additionally, we can execute the docker volume inspect <volume> command to display detailed information pertaining to a volume:
[
{
"CreatedAt": "2019-12-28T12:52:52+05:30",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/77db32d66407a554
bd0dbdf3950671b658b6233c509eaed9f5c2a589fea268fe/_data",
"Name": "77db32d66407a554bd0dbdf3950671b658b6233c509eae
d9f5c2a589fea268fe",
"Options": null,
"Scope": "local"
}
]
This is also similar to the previous output, with the same unique name and the mount path of the volume.
In the next exercise, we will learn how to use the VOLUME directive in a Dockerfile.
Exercise 2.06: Using VOLUME Directive in the Dockerfile
In this exercise, you will be setting a Docker container to run the Apache web server. However, you do not want to lose the Apache log files in case of a Docker container failure. As a solution, you have decided to persist in the log files by mounting the Apache log path to the underlying Docker host:
- Create a new directory named
volume-exercise:mkdir volume-exercise
- Navigate to the newly created
volume-exercisedirectory:cd volume-exercise
- Within the
volume-exercisedirectory, create a file namedDockerfile:touch Dockerfile
- Now, open the
Dockerfileusing your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile, save it, and exit from theDockerfile:# VOLUME example FROM ubuntu RUN apt-get update && apt-get install apache2 -y VOLUME ["/var/log/apache2"]
This
Dockerfilestarted by defining the Ubuntu image as the parent image. Next, you will execute theapt-get updatecommand to update the package list, and theapt-get install apache2 -ycommand to install the Apache web server. Finally, use theVOLUMEdirective to set up a mount point to the/var/log/apache2directory. - Now, build the Docker image:
$ docker image build -t volume .
The output should be as follows:

Figure 2.9: Building the volume Docker image
- Execute the docker container run command to start a new container from the Docker image that you built in the previous step. Note that you are using the
--interactiveand--ttyflags to open an interactive bash session so that you can execute commands from the bash shell of the Docker container. You have also used the--nameflag to define the container name asvolume-container:$ docker container run --interactive --tty --name volume-container volume /bin/bash
Your bash shell will be opened as follows:
root@bc61d46de960: /#
- From the Docker container command line, change directory to the
/var/log/apache2/directory:# cd /var/log/apache2/
This will produce the following output:
root@bc61d46de960: /var/log/apache2#
- Now, list the available files in the directory:
# ls -l
The output should be as follows:

Figure 2.10: Listing files of the /var/log/apache2 directory
These are the log files created by Apache while running the process. The same files should be available once you check the host mount of this volume.
- Now, exit the container to check the host filesystem:
# exit
- Inspect
volume-containerto view the mount information:$ docker container inspect volume-container
Under the "
Mounts" key, you can see the information relating to the mount:
Figure 2.11: Inspecting the Docker container
- Inspect the volume with the
docker volume inspect <volume_name>command.<volume_name>can be identified by theNamefield of the preceding output:$ docker volume inspect 354d188e0761d82e1e7d9f3d5c6ee644782b7150f51cead8f140556e5d334bd5
You should get the output similar to the following:

Figure 2.12: Inspecting the Docker volume
We can see that the container is mounted to the host path of
"/var/lib/docker/volumes/354d188e0761d82e1e7d9f3d5c6ee644782b 7150f51cead8f140556e5d334bd5/_data", which is defined as theMountpointfield in the preceding output. - List the files available in the host file path. The host file path can be identified with the
"Mountpoint" field of the preceding output:$ sudo ls -l /var/lib/docker/volumes/354d188e0761d82e1e7d9f3d5c6ee644782b7150f51cead8f14 0556e5d334bd5/_data
In the following output, you can see that the log files in the
/var/log/apache2directory of the container are mounted to the host:
Figure 2.13: Listing files in the mount point directory
In this exercise, we observed how to mount the log path of the Apache web server to the host filesystem using the VOLUME directive. In the next section, we will learn about the EXPOSE directive.
The EXPOSE Directive
The EXPOSE directive is used to inform Docker that the container is listening on the specified ports at runtime. We can use the EXPOSE directive to expose ports through either TCP or UDP protocols. The EXPOSE directive has the following format:
EXPOSE <port>
However, the ports exposed with the EXPOSE directive will only be accessible from within the other Docker containers. To expose these ports outside the Docker container, we can publish the ports with the -p flag with the docker container run command:
docker container run -p <host_port>:<container_port> <image>
As an example, imagine that we have two containers. One is a NodeJS web app container that should be accessed from outside via port 80. The second one is the MySQL container, which should be accessed from the node app container via port 3306. In this scenario, we have to expose port 80 of the NodeJS app with the EXPOSE directive and use the -p flag with the docker container run command to expose it externally. However, for the MySQL container, we can only use the EXPOSE directive without the -p flag when running the container, as 3306 should only be accessible from the node app container.
So, in summary, the following statements define this directive:
- If we specify both the
EXPOSEdirective and-pflag, exposed ports will be accessible from other containers as well as externally. - If we specify
EXPOSEwithout the-pflag, exposed ports will only be accessible from other containers, but not externally.
You will learn about the HEALTHCHECK directive in the next section.
The HEALTHCHECK Directive
Health checks are used in Docker to check whether the containers are running healthily. For example, we can use health checks to make sure the application is running within the Docker container. Unless there is a health check specified, there is no way for Docker to say whether a container is healthy. This is very important if you are running Docker containers in production environments. The HEALTHCHECK directive has the following format:
HEALTHCHECK [OPTIONS] CMD command
There can be only one HEALTHCHECK directive in a Dockerfile. If there is more than one HEALTHCHECK directive, only the last one will take effect.
As an example, we can use the following directive to ensure that the container can receive traffic on the http://localhost/ endpoint:
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
The exit code at the end of the preceding command is used to specify the health status of the container. 0 and 1 are valid values for this field. 0 is used to denote a healthy container, and 1 is used to denote an unhealthy container.
In addition to the command, we can specify a few other parameters with the HEALTHCHECK directive, as follows:
--interval: This specifies the period between each health check (the default is 30s).--timeout: If no success response is received within this period, the health check is considered failed (the default is 30s).--start-period: The duration to wait before running the first health check. This is used to give a startup time for the container (the default is 0s).--retries: The container will be considered unhealthy if the health check failed consecutively for the given number of retries (the default is 3).
In the following example, we have overridden the default values by providing our custom values with the HEALTHCHECK directive:
HEALTHCHECK --interval=1m --timeout=2s --start-period=2m --retries=3 \ CMD curl -f http://localhost/ || exit 1
We can check the health status of a container with the docker container list command. This will list the health status under the STATUS column:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d4e627acf6ec sample "apache2ctl -D FOREG…" About a minute ago Up About a minute (healthy) 0.0.0.0:80->80/tcp upbeat_banach
As soon as we start the container, the health status will be health: starting. Following the successful execution of the HEALTHCHECK command, the status will change to healthy.
In the next exercise, we are going to use the EXPOSE and HEALTHCHECK directives to create a Docker container with the Apache web server and define health checks for it.
Exercise 2.07: Using EXPOSE and HEALTHCHECK Directives in the Dockerfile
Your manager has asked you to dockerize the Apache web server to access the Apache home page from the web browser. Additionally, he has asked you to configure health checks to determine the health status of the Apache web server. In this exercise, you will use the EXPOSE and HEALTHCHECK directives to achieve this goal:
- Create a new directory named
expose-healthcheck:mkdir expose-healthcheck
- Navigate to the newly created
expose-healthcheckdirectory:cd expose-healthcheck
- Within the
expose-healthcheckdirectory, create a file namedDockerfile:touch Dockerfile
- Now, open the
Dockerfileusing your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile, save it, and exit from theDockerfile:# EXPOSE & HEALTHCHECK example FROM ubuntu RUN apt-get update && apt-get install apache2 curl -y HEALTHCHECK CMD curl -f http://localhost/ || exit 1 EXPOSE 80 ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
This
Dockerfilefirst defines the ubuntu image as the parent image. Next, we execute theapt-get updatecommand to update the package list, and theapt-get install apache2 curl -ycommand to install the Apache web server and curl tool.Curlis required to execute theHEALTHCHECKcommand. Next, we define theHEALTHCHECKdirective with curl to thehttp://localhost/endpoint. Then, we exposed port80of the Apache web server so that we can access the home page from our web browser. Finally, we start the Apache web server with theENTRYPOINTdirective. - Now, build the Docker image:
$ docker image build -t expose-healthcheck.
You should get the following output:

Figure 2.14: Building the expose-healthcheck Docker image
- Execute the docker container run command to start a new container from the Docker image that you built in the previous step. Note that you are using the
-pflag to redirect port80of the host to port80of the container. Additionally, you have used the--nameflag to specify the container name asexpose-healthcheck-container, and the-dflag to run the container in detached mode (this runs the container in the background):$ docker container run -p 80:80 --name expose-healthcheck-container -d expose-healthcheck
- List the running containers with the
docker container listcommand:$ docker container list
In the following output, you can see that the
STATUSof theexpose-healthcheck-containeris healthy:
Figure 2.15: List of running containers
- Now, you should be able to view the Apache home page. Go to the
http://127.0.0.1endpoint from your favorite web browser:
Figure 2.16: Apache home page
- Now, clean up the container. First, stop the Docker container by using the
docker container stopcommand:$ docker container stop expose-healthcheck-container
- Finally, remove the Docker container with the
docker container rmcommand:$ docker container rm expose-healthcheck-container
In this exercise, you utilized the EXPOSE directive to expose an Apache web server as a Docker container and used the HEALTHCHECK directive to define a health check to verify the healthy status of the Docker container.
In the next section, we will learn about the ONBUILD directive.
The ONBUILD Directive
The ONBUILD directive is used in the Dockerfile to create a reusable Docker image that will be used as the base for another Docker image. As an example, we can create a Docker image that contains all the prerequisites, such as dependencies and configurations, in order to run an application. Then, we can use this 'prerequisite' image as the parent image to run the application.
While creating the prerequisite image, we can use the ONBUILD directive, which will include the instructions that should only be executed when this image is used as the parent image in another Dockerfile. ONBUILD instructions will not be executed while building the Dockerfile that contains the ONBUILD directive, but only when building the child image.
The ONBUILD directive takes the following format:
ONBUILD <instruction>
As an example, consider that we have the following ONBUILD instruction in the Dockerfile of our custom base image:
ONBUILD ENTRYPOINT ["echo","Running ONBUILD directive"]
The "Running ONBUILD directive" value will not be printed if we create a Docker container from our custom base image. However, the "Running ONBUILD directive" value will be printed if we use our custom base image as the base for our new child Docker image.
We can use the docker image inspect command for the parent image to list the OnBuild triggers listed for the image:
$ docker image inspect <parent-image>
The command will return output similar to the following:
... "OnBuild": [ "CMD [\"echo\",\"Running ONBUILD directive\"]" ] ...
In the next exercise, we will be using the ONBUILD directive to define a Docker image to deploy the HTML files.
Exercise 2.08: Using ONBUILD Directive in the Dockerfile
You have been asked by your manager to create a Docker image that is capable of running any HTML files provided by the software development team. In this exercise, you will build a parent image with the Apache web server and use the ONBUILD directive to copy the HTML files. The software development team can use this Docker image as the parent image to deploy and test any HTML files created by them:
- Create a new directory named
onbuild-parent:mkdir onbuild-parent
- Navigate to the newly created
onbuild-parentdirectory:cd onbuild-parent
- Within the
onbuild-parentdirectory, create a file namedDockerfile:touch Dockerfile
- Now, open the
Dockerfileusing your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile, save it, and exit from theDockerfile:# ONBUILD example FROM ubuntu RUN apt-get update && apt-get install apache2 -y ONBUILD COPY *.html /var/www/html EXPOSE 80 ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
This
Dockerfilefirst defines the ubuntu image as the parent image. It then executes theapt-get updatecommand to update the package list, and theapt-get install apache2 -ycommand to install the Apache web server. TheONBUILDdirective is used to provide a trigger to copy all HTML files to the/var/www/htmldirectory. TheEXPOSEdirective is used to expose port80of the container andENTRYPOINTto start the Apache web server using theapache2ctlcommand. - Now, build the Docker image:
$ docker image build -t onbuild-parent .
The output should be as follows:

Figure 2.17: Building the onbuild-parent Docker image
- Execute the
docker container runcommand to start a new container from the Docker image built in the previous step:$ docker container run -p 80:80 --name onbuild-parent-container -d onbuild-parent
In the preceding command, you have started the Docker container in detached mode while exposing port
80of the container. - Now, you should be able to view the Apache home page. Go to the
http://127.0.0.1endpoint from your favorite web browser. Note that the default Apache home page is visible:
Figure 2.18: Apache home page
- Now, clean up the container. Stop the Docker container by using the
docker container stopcommand:$ docker container stop onbuild-parent-container
- Remove the Docker container with the
docker container rmcommand:$ docker container rm onbuild-parent-container
- Now, create another Docker image using
onbuild-parent-containeras the parent image to deploy a custom HTML home page. First, change the directory back to the previous directory:cd ..
- Create a new directory named
onbuild-childfor this exercise:mkdir onbuild-child
- Navigate to the newly created
onbuild-childdirectory:cd onbuild-child
- Within the
onbuild-childdirectory, create a file namedindex.html. This file will be copied to the Docker image by theONBUILDcommand during build time:touch index.html
- Now, open the
index.htmlfile using your favorite text editor:vim index.html
- Add the following content to the
index.htmlfile, save it, and exit from theindex.htmlfile:<html> <body> <h1>Learning Docker ONBUILD directive</h1> </body> </html>
This is a simple HTML file that will output the
Learning Docker ONBUILDdirective as the header of the page. - Within the
onbuild-childdirectory, create a file namedDockerfile:touch Dockerfile
- Now, open the
Dockerfileusing your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile, save it, and exit from theDockerfile:# ONBUILD example FROM onbuild-parent
This
Dockerfilehas only one directive. This will use theFROMdirective to utilize theonbuild-parentDocker image that you created previously as the parent image. - Now, build the Docker image:
$ docker image build -t onbuild-child .

Figure 2.19: Building the onbuild-child Docker image
- Execute the
docker container runcommand to start a new container from the Docker image that you built in the previous step:$ docker container run -p 80:80 --name onbuild-child-container -d onbuild-child
In this command, you have started the Docker container from the
onbuild-childDocker image while exposing port80of the container. - You should be able to view the Apache home page. Go to the
http://127.0.0.1endpoint from your favorite web browser:
Figure 2.20: Customized home page of the Apache web server
- Now, clean up the container. First, stop the Docker container by using the
docker container stopcommand:$ docker container stop onbuild-child-container
- Finally, remove the Docker container with the
docker container rmcommand:$ docker container rm onbuild-child-container
In this exercise, we observed how we can use the ONBUILD directive to create a reusable Docker image that is capable of running any HTML file provided to it. We created the reusable Docker image named onbuild-parent with the Apache web server, with port 80 exposed. This Dockerfile contains the ONBUILD directive to copy the HTML files in the context of the Docker image. Then, we created the second Docker image named onbuild-child, using onbuild-parent as the base image, that provided a simple HTML file to be deployed to the Apache web server.
Now, let's test our knowledge that we have acquired in this chapter by dockerizing the given PHP application using the Apache web server in the following activity.
Activity 2.01: Running a PHP Application on a Docker Container
Imagine that you want to deploy a PHP welcome page that will greet visitors based on the date and time using the following logic. Your task is to dockerize the PHP application given here, using the Apache web server installed on an Ubuntu base image:
<?php
$hourOfDay = date('H');
if($hourOfDay < 12) {
$message = "Good Morning";
} elseif($hourOfDay > 11 && $hourOfDay < 18) {
$message = "Good Afternoon";
} elseif($hourOfDay > 17){
$message = "Good Evening";
}
echo $message;
?>
This is a simple PHP file that will greet the user based on the following logic:
Figure 2.21: Logic of PHP application
Execute the following steps to complete this activity:
- Create a folder to store the activity files.
- Create a
welcome.phpfile with the code provided previously. - Create a
Dockerfileand set up the application with PHP and Apache2 on an Ubuntu base image. - Build and run the Docker image.
- Once completed, stop and remove the Docker container.
Note
The solution for this activity can be found via this link.