Dockerfiles are a powerful way to automate the deployment of applications on a server. They allow you to specify all of the steps needed to build an application, including the source code, operating system images, and any other dependencies, and then have Docker take care of everything else. One common use for Dockerfiles is to create a custom image that contains the software you need to run your application. For example, you might create a custom image that contains the operating system and all of the applications you need to run your business. You can also use Dockerfiles to deploy applications on servers without installing them first. One common use for custom images is to deploy applications on servers without installing them first. The docker build command takes two arguments: a Dockerfile and an image name. The docker build command creates an image based on the contents of the Dockerfile . The image name is simply a name for the resulting image; it doesn’t contain any information about what’s inside the image. You can view an example of a docker build command in action by running this command: docker build -t my-app . This command builds an image called my-app . To view this image, you can run this command: docker images | grep my-app ..
Docker volumes are used to store persistent data outside your containers. They allow config files, databases, and caches used by your application to outlive individual container instances.
Volumes can be mounted when you start containers with the docker run command’s -v flag. This can either reference a named volume or bind mount a host directory into the container’s filesystem.
It’s also possible to define volumes at image build time by using the VOLUME instruction in your Dockerfiles. This mechanism guarantees that containers started from the image will have persistent storage available. In this article you’ll learn how to use this instruction and the use cases where it makes sense.
Defining Volumes In Dockerfiles
The Dockerfile VOLUME instruction creates a volume mount point at a specified container path. A volume will be mounted from your Docker host’s filesystem each time a container starts.
The Dockerfile in the following example defines a volume at the /opt/app/data container path. New containers will automatically mount a volume to the directory.
Build your image so you can test the volume mount:
Retrieve the list of existing volumes as a reference:
Now start a container using your test image:
Repeat the docker volume ls command to confirm a new volume has been created:
Exit out of your test container’s shell so that the container stops:
The volume and its data will continue to persist:
You can define multiple volumes in one instruction as a space-delimited string or a JSON array. Both of the following forms create and mount two unique volumes when containers start:
Populating Initial Volume Content
Volumes are automatically populated with content placed into the mount directory by previous image build steps:
This Dockerfile defines a volume that will be initialized with the existing default-config.yaml file. The container will be able to read /opt/app/config/default-config.yaml without having to check whether the file exists.
Changes to a volume’s content made after the VOLUME instruction will be discarded. In this example, the default-config.yaml file is still available after containers start because the rm command comes after /opt/app/config is marked as a volume.
Overriding VOLUME Instructions When Starting a Container
Volumes created by the VOLUME instruction are automatically named with a long unique hash. It’s not possible to change their names so it can be difficult to identify which volumes are actively used by your containers.
You can prevent these volumes appearing by manually defining volumes on your containers with docker run -v as usual. The following command explicitly mounts a named volume to the container’s /opt/app/config directory, making the Dockerfile’s VOLUME instruction redundant.
When Should You Use VOLUME Instructions?
VOLUME instructions can be helpful in situations where you want to enforce that persistence is used, such as in images that package a database server or file store. Using VOLUME instructions makes it easier to start containers without remembering the -v flags to apply.
VOLUME also serves as documentation of the container paths that store persistent data. Including these instructions in your Dockerfile allows anyone to determine where your container keeps its data, even if they’re unfamiliar with your application.
VOLUME Pitfalls
VOLUME isn’t without its drawbacks. Its biggest problem is how it interacts with image builds. Using an image with a VOLUME instruction as your build’s base image will behave unexpectedly if you change content within the volume mount point.
The gotcha from earlier still applies: the effects of commands after the VOLUME instruction will be discarded. As VOLUME will reside in the base image, everything in your own Dockerfile comes after the instruction and you’re unable to modify the directory’s default contents. Behind the scenes, starting the temporary container for the build will create a new volume on your host that gets destroyed at the end of the build. Changes will not be copied back to the output image.
Automatic volume mounting can be problematic in other situations too. Sometimes users might prefer to start a temporary container without any volumes, perhaps for evaluation or debugging purposes. VOLUME removes this possibility as it’s not possible to disable the automatic mounts. This causes many redundant volumes to accumulate on the host if containers that use the instruction are regularly started.
Summary
Dockerfile VOLUME instructions allow volume mounts to be defined at image build time. They guarantee that containers started from the image will have persistent data storage available, even if the user omits the docker run command’s -v flag.
This behavior can be useful for images where persistence is critical or many volumes are needed. However the VOLUME instruction also breaks some user expectations and introduces unique behaviors so it needs to be written with care. Providing a Docker Compose file that automatically creates required volumes is often a better solution.