Docker Dockerfile

Jakob Jenkov
Last update: 2019-10-19

A Docker Dockerfile contains a set of instructions for how to build a Docker image. The Docker build command executes the Dockerfile and builds a Docker image from it. This Dockerfile tutorial explains how to write a Dockerfile and build it into a Docker image.

Dockerfile Benefits

A Dockerfile captures the recipe for building a Docker image in writing. A Docker image typically consists of:

  • A base Docker image on top of which to build your own Docker image.
  • A set of tools and applications to be installed in the Docker image.
  • A set of files to be copied into the Docker image (e.g configuration files).
  • Possibly a network (TCP / UDP) port (or more) to be opened for traffic in the firewall.
  • etc.

First of all, capturing all this in writing in a Dockerfile means that You don't have to remember how to setup a new installation of your application, with OS requirements, applications to install, files to copy, network ports to open up for etc. It's all described in the Dockerfile.

Additionally, letting Docker build your Docker image from a Dockerfile means that you don't have to do this manual, tedious and error prone job. Docker takes of it for you, so you can do something else in the meantime. This is both easier, faster, and less error prone.

Third, it's really easy to share a Dockerfile with someone else, and have them be able to build the Docker image.

Fourth, a Dockerfile can easily be stored in a version control system like Git, so you can track changes to the Dockerfile (the server and application configuration) over time. Version control systems typically also makes it easier for multiple people to collaborate - e.g. on a Dockerfile, as well as share the Dockerfile later on.

Dockerfile Structure

A Dockerfile consists of a set of instructions. Each instruction consists of a command followed by arguments to that command, similar to command line executables. Here is a simple example of a Dockerfile:

# The base image
FROM ubuntu:latest

# More instructions here that install software and copy files into the image.
COPY    /myapp/target/myapp.jar    /myapp/myapp.jar

# The command executed when running a Docker container based on this image.
CMD echo Starting Docker Container

Docker Base Image

A Docker image consists of layers. Each layer adds something to the final Docker image. Each layer is actually a separate Docker image. Thus, your Docker image consists of one or more underlying Docker images, on top of which you add your own layers.

When you specify your own Docker image via a Dockerfile you typically start with a Docker base image. This is another Docker image on top of which you want your own Docker image to be built. The Docker base image you are using may itself consist of multiple layers, and can itself be based on another base image etc., until you get down to the most basic Docker image you can create - a raw Linux container image with no special settings applied.

You specify the base image of a Docker image in your Dockerfile using the FROM command, as explained in the following section.

MAINTAINER

The Dockerfile MAINTAINER command is simply used to tell who is maintaining this Dockerfile. Here is an example:

MAINTAINER   Joe Blocks <joe@blocks.com>

The MAINTAINER instruction is not often used though, since that kind of information is also often available in GIT repositories and elsewhere.

FROM

The Dockerfile FROM command specifies the base image of your Docker images. If you want to start with a bare Linux image, you can use this FROM command:

# The base image
FROM ubuntu:latest

CMD

The CMD command specifies the command line command to execute when a Docker container is started up which is based on the Docker image built from this Dockerfile. Here are a few Dockerfile CMD examples:

CMD echo Docker container started.

This example just prints the text Docker container started when the Docker container is started.

The next CMD example runs a Java application:

CMD java -cp /myapp/myapp.jar com.jenkov.myapp.MainClass arg1 arg2 arg3

COPY

The Dockerfile COPY command copies one or more files from the Docker host (the computer building the Docker image from the Dockerfile) into the Docker image. The COPY command can copy both a file or a directory from the Docker host to the Docker image. Here is a Dockerfile COPY example:

COPY    /myapp/target/myapp.jar    /myapp/myapp.jar

This example copies a single file from the Docker host at /myapp/target/myapp.jar to the Docker image at /myapp/myapp.jar . The first argument is the Docker host path (where to copy from) and the second argument is the Docker image path (where to copy to).

You can also copy a directory from the Docker host to the Docker image. Here is an example:

COPY    /myapp/config/prod    /myapp/config

This example copies the directory /myapp/config/prod from the Docker host to the /myapp/config directory in the Docker image.

You can als copy multiple files into a single directory in the Docker image using the COPY command. Here is an example:

COPY    /myapp/config/prod/conf1.cfg   /myapp/config/prod/conf2.cfg   /myapp/config/

This example copies the two files /myapp/config/prod/conf1.cfg and /myapp/conig/prod/conf2.cfg into the Docker image directory /myapp/config/ . Notice how the destination directory has to end with a / (slash) for this to work.

ADD

The Dockerfile ADD instruction works in the same way as the COPY instruction with a few minor differences:

  • The ADD instruction can copy and extract TAR files from the Docker host to the Docker image.
  • The ADD instruction can download files via HTTP and copy them into the Docker image.

Here are a few Dockerfile ADD examples:

ADD    myapp.tar    /myapp/

This example will extract the given TAR file from the Docker host into the /myapp/ directory inside the Docker image.

Here is another example:

ADD    http://jenkov.com/myapp.jar    /myapp/

ENV

The Dockerfile ENV command can set an environment variable inside the Docker image. This environment variable is available for the application that is started inside the Docker image with the CMD command. Here is an example:

ENV    MY_VAR   123

This example sets the environment variable MY_VAR to the value 123 .

RUN

The Dockerfile RUN command can execute command line executables within the Docker image. The RUN command is executed during build time of the Docker image, so RUN commands are only executed once. The RUN command can be used to install applications within the Docker image, or extract files, or other command line activities which are necessary to run once to prepare the Docker image for execution.

RUN apt-get install some-needed-app

ARG

The Dockerfile ARG instruction lets you define an argument which can be passed to Docker when you build the Docker image from the Dockerfile. Here is an example:

ARG tcpPort

When you run the Docker command to build the Dockerfile containing the above ARG instruction, you can pass an argument to the tcpPort argument like this:

docker build --build-arg tcpPort=8080 .

Notice the --build-arg followed by the tcpPort=8080 . This part sets the tcpPort argument value to 8080.

You can define multiple build arguments using multiple ARG instructions. Here is an example:

ARG tcpPort
ARG useTls

When building the Docker image you must provide values for all of the build arguments. You do so by repeating the --build-arg sections for each argument you want to set. Here is an example:

docker build --build-arg tcpPort=8080 --build-arg useTls=true .

You can set a default value for an ARG so that it becomes optional to provide a value for it when building the Docker image. If the argument is not given a value, it will be given its default value. Here is an example:

ARG tcpPort=8080
ARG useTls=true

If neither the tcpPort nor the useTls argument is set when building the Docker image for the Dockerfile containing the above ARG instructions, their argument values will be set to 8080 and true .

Arguments declared by ARG are typically referenced elsewhere in your Dockerfile. You reference an ARG argument like this:

ARG tcpPort=8080
ARG useTls=true

CMD start-my-server.sh -port ${tcpPort} -tls ${useTls}

Notice the two references ${tcpPort} and ${useTls}. These refer to the declared ARG arguments named tcpPort and useTls .

docker build --build-arg tcpPort=8080

WORKDIR

The WORKDIR instruction specifies what the working directory should be inside the Docker image. The working directory will be in effect for all commands following the WORKDIR instruction. Here is an example:

WORKDIR    /java/jdk/bin

EXPOSE

The Dockerfile EXPOSE instruction opens up network ports in the Docker container to the outside world. For instance, if your Docker container runs a web server, that web server will probably need port 80 open for any client to be able to connect to it. Here is an example of opening a network port using the EXPOSE command:

EXPOSE   8080

You can also set which protocol is allowed to communicate on the opened port. For instance, UDP or TCP. Here is an example of setting the allowed protocol also:

EXPOSE   8080/tcp 9999/udp

If no protocol is set (after the / ) , then the protocol is assumed to be TCP.

VOLUME

The Dockerfile VOLUME instruction creates a directory inside the Docker image which you can later mount a volume (directory) to from the Docker host. In other words, you can create a directory inside the docker image, e.g. called /data which can later be mounted to a directory, e.g. called /container-data/container1 in the Docker host. The mounting is done when the container is started up. Here is an example of defining a volume (mountable directory) in a Dockerfile using the VOLUME instruction:

VOLUME   /data

ENTRYPOINT

The Dockerfile ENTRYPOINT instruction provides an entrypoint for Docker containers started from this Docker image. An entrypoint is an application or command that is executed when the Docker container is started up. In that way, ENTRYPOINT works similarly to CMD, with the difference being that using ENTRYPOINT the Docker container is shut down when the application executed by ENTRYPOINT finishes. Thus, ENTRYPOINT kind of makes your Docker image an executable command itself, which can be started up and which shut down automatically when finished. Here is a Dockerfile ENTRYPOINT example:

ENTRYPOINT java -cp /apps/myapp/myapp.jar com.jenkov.myapp.Main

This example will execute the Java application main class com.jenkov.myapp.Main when the Docker container is started up, and when the application shuts down, so does the Docker container.

HEALTHCHECK

The Dockerfile HEALTHCHECK instruction can execute a health check command line command at regular intervals, to monitor the health of the application running inside the Docker container. If the command line command returns a value of 0 when exiting, Docker considers the application and container to be healthy. If the command line command returns a value of 1, Docker consider the application and container to be unhealthy. Here is a Dockerfile HEALTHCHECK example:

HEALTHCHECK java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck

This example configures the Java application com.jenkov.myapp.HealthCheck as the healthcheck command. You can use whatever healthcheck command that makes sense to you here.

Health Check Interval

By default Docker executes the HEALTHCHECK command every 30 seconds. However, you can set a custom health check interval if you prefer a different interval length than the default 30 seconds. You specify the health check interval using the --interval argument to the HEALTHCHECK instruction. Here is an example that sets the HEALTHCHECK interval to 60 seconds instead:

HEALTHCHECK --interval=60s java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck

Health Check Start Period

By default Docker will start checking the health of the Docker container immediately. However, sometimes an application might take some time to start up, so it may not make sense to health check it until after a certain time period has elapsed. This gives the application the chance to startup before Docker starts health checking it. You can set the health check start period using the --start-period argument to the HEALTHCHECK instruction. Here is an example setting the health check start period to 5 minutes, giving the container and application 300 seconds (5 minutes) to start up before Docker starts checking its health:

HEALTHCHECK --start-period=300s java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck

Health Check Timeout

It is possible for a health check to time out. If the HEALTCHECK command takes more than a given time limit to finish, Docker will consider the health check timed out. You can set the timeout limit using the --timeout argument to the HEALTHCHECK command. Here is an example of setting the health check timeout time limit to 5 seconds:

HEALTHCHECK --timeout=5s java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck

Note: If the health check times out, Docker considers the container to be unhealthy too.

Health Check Retries

If the HEALTHCHECK fails, either because the HEALTCHECK command returns 1, or if it times out, Docker will retry the HEALTHCHECK command 3 times to see if the Docker container returns to a healthy state, before considering the Docker container unhealthy. You can override the default 3 retries using the --retries argument to the HEALTHCHECK instruction. Here is an example of setting the number of health check retries to 5:

HEALTHCHECK --retries=5 java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck

Jakob Jenkov

Featured Videos

Java Generics

Java ForkJoinPool

P2P Networks Introduction



















Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next