Build a Docker Image

How does a Docker image work?

We’ve already seen that Docker images are read-only templates from which Docker containers are launched. Each image consists of a series of layers. Docker makes use of union file systems to combine these layers into a single image. Union file systems allow files and directories of separate file systems, known as branches, to be transparently overlaid, forming a single coherent file system.

One of the reasons Docker is so lightweight is because of these layers. When you change a Docker image—for example, update an application to a new version— a new layer gets built. Thus, rather than replacing the whole image or entirely rebuilding, as you may do with a virtual machine, only that layer is added or updated. Now you don’t need to distribute a whole new image, just the update, making distributing Docker images faster and simpler.

Every image starts from a base image, for example ubuntu, a base Ubuntu image, or fedora, a base Fedora image. You can also use images of your own as the basis for a new image, for example if you have a base Apache image you could use this as the base of all your web application images.

Docker images are then built from these base images using a simple, descriptive set of steps we call instructions. Each instruction creates a new layer in our image. Instructions include actions like:

  • Run a command.
  • Add a file or directory.
  • Create an environment variable.
  • What process to run when launching a container from this image?

These instructions are stored in a file called a Dockerfile. Docker reads this Dockerfile when you request a build of an image, executes the instructions, and returns a final image.

Steps to build Dockerfile:

Dockerfile :

Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

Each Dockerfile is a script, composed of various commands (instructions) and arguments listed successively to automatically perform actions on a base image in order to create (or form) a new image.

Here is the format of the Dockerfile:

# Comment


The instruction is not case-sensitive; however convention is for them to be UPPERCASE in order to distinguish them from arguments more easily.

Docker runs the instructions in a Dockerfile in order. The first instruction must be `FROM` in order to specify the Base Image from which you are building.

Docker will treat lines that begin with # as a comment. A # marker anywhere else in the line will be treated as an argument.

List of instruction keywords we can use in Dockerfile :

  • ADD
  • CMD
  • COPY
  • ENV
  • FROM
  • RUN
  • USER


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>.


# Usage: ADD [source file or directory or URL] [destination directory]

ADD test /DataDir/       # source file to Destination directory

ADD /mydata /DaraDir/    # source directory to destination directory



The command CMD is not executed during build, but when a container is instantiated using the image being built.

The main purpose of a CMD is to provide defaults for an executing container.

There can only be one CMD instruction in a Dockerfile. If you list more than one CMD then only the last CMD will take effect.

an example for CMD would be running an application upon creation of a container which is already installed inside the image.

Example :

# Usage 1: CMD application “argument”, “argument”, ..

CMD “/etc/init.d/httpd” “start”


The COPY instruction copies new files or directories from <src> and adds them to the filesystem of the container at the path <dest>.

# Usage: ADD [source file or directory or URL] [destination directory]

COPY test /DataDir/       # source file to Destination directory

COPY /mydata /DaraDir/    # source directory to destination directory



Specifies the command that a container created from the image always runs. In this example, the command is /usr/sbin/httpd -D FOREGROUND, which starts the HTTP server process.


The ENV instruction sets the environment variables. These variables consist of <key> to the value <value>.


# Usage: ENV key value

ENV JAVA_HOME /usr/bin/java



Defines that the specified port is available to service incoming requests. You can use the -p or -P options with docker run to map this port to another port on the host. Alternatively, you can use the –link option with docker run to allow another container to access the port over Docker’s internal network


# Usage: EXPOSE [port]




Defines the image that Docker uses as a basis for the new image(An image that has no parent is a base image).


# Usage: FROM [image name]

FROM centos



Defines who is responsible for the Dockerfile.


# Usage: MAINTAINER [name]




Defines the commands that Docker runs to modify the new image. In the example, the RUN lines set up the web proxy, install the httpd package, and create a simple home page for the server.

# Usage: RUN [command]

RUN yum install -y mysql-server


The USER instruction sets the user name or UID to use when running the image and for any RUN, CMD and ENTRYPOINT instructions that follow it in the Dockerfile.


# Usage: USER [UID]

USER 2222


The VOLUME instruction makes the directory available as a volume that other containers can mount by using the –volumes-from option to docker run.


# Usage: VOLUME [“/dir1”, “/dir2” ..]

VOLUME [“/myvol”]




The WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile.

The WORKDIR instruction can resolve environment variables previously set using ENV. You can only use environment variables explicitly set in the Dockerfile. For example:

Example :



RUN pwd


The output of the final pwd command in this Dockerfile would be /path/$DIRNAME

Samples of Dockerfile’s :


RUN yum install -y mysql mysql-server
RUN service mysqld start && mysqladmin -u root password mysql123 && \
  echo "GRANT ALL ON *.* TO admin@'%' IDENTIFIED BY 'changeme' WITH GRANT OPTION; \
                FLUSH PRIVILEGES" | mysql -u root -pmysql123 &&  service mysqld stop
CMD ["/usr/bin/mysqld_safe"]




# Dockerfile that modifies oraclelinux:6.6 to include an Apache HTTP server
FROM oraclelinux:6.6
RUN yum -y install httpd
RUN echo "HTTP server running on docker" > /var/www/html/index.html


Use the docker build command to create the image from Dockerfile.

# docker build –tag=”centos/httpd:v2″ –file=”/var/docker_projects/centos/httpd/Dockerfile”

 # docker run -d –name newguest2 -P centos/httpd:v2


You do not need to specify /usr/sbin/httpd -D FOREGROUND as this command is now built into the container.

The -P option specifies that Docker should map the ports exposed by the guest to available ports in the range 49000 through 49900 on the host.

You can use docker inspect to return the host port that Docker maps to TCP port 80:

[root@host ~]# docker inspect –format='{{ .NetworkSettings.Ports }}’ newguest2 

map[80/tcp:[map[HostIp: HostPort:49153]]]

In this example, TCP port 80 in the guest is mapped to TCP port 49153 on the host.

# docker run -i -t –name guest oraclelinux:6.6 /bin/bash

The –name option specifies the name guest for the container instance. Docker does not remove the container when it exits and we can restart it at a later time.