Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

We mount Docker volumes when we need to link container resources to a host. We can use different volumes, like named volumes or bind mounts. Moreover, whether they are persistent or not, we can use local or remote resources. However, when mounting, we may need to exclude, for example, some files or folders that aren't required.

In this tutorial, we'll learn how to exclude folders when mounting volumes with some Docker Compose examples.

2. Create a Nodejs Docker Image

So why do we need to exclude some files or a folder with Docker? First, let's discuss Docker images.

When we build an image, we usually add application files. To demonstrate, we'll use Nodejs to create a Docker sample application.

Once we set the main application, let's have a look at our Dockerfile:

FROM node:12.18.1
ENV NODE_ENV=production

WORKDIR /app

COPY ["package.json", "package-lock.json*", "./"]

RUN npm install --production

COPY . .

CMD [ "node", "server.js" ]

Now we can build the image which we'll call, for instance, node-docker:

$ docker build -t node-docker .

In this case, the build context is our local /app folder. However, it could be a set of files located in the specified path or URL, like a Git repository, for example.

When we build our Docker image, we'll send the files to a Docker server where the image is stored. Docker creates a layered image based on commands such as copy or run.

Let's run the docker history command to see the different layers of an image:

$ docker history --format "ID-> {{.ID}} | Created-> {{.CreatedSince}} | Created By-> {{.CreatedBy}} | Size: {{.Size}}" e870a50eed97

We can use the –format option to create a custom output and show info like the relevant commands or the size:

ID-> e870a50eed97 | Created-> 36 hours ago | Created By-> /bin/sh -c #(nop)  CMD ["node" "server.js"] | Size: 0B
ID-> 708a43cd0ef2 | Created-> 36 hours ago | Created By-> /bin/sh -c #(nop) COPY dir:7cc2842dd32649457… | Size: 11.3MB
ID-> d49b84f48e41 | Created-> 36 hours ago | Created By-> /bin/sh -c npm install --production | Size: 14.7MB
ID-> a351be0717a1 | Created-> 36 hours ago | Created By-> /bin/sh -c #(nop) COPY multi:9959dc16241ba60… | Size: 80.7kB
ID-> 56b22d35f315 | Created-> 36 hours ago | Created By-> /bin/sh -c #(nop) WORKDIR /app | Size: 0B
ID-> c28b64493ce8 | Created-> 36 hours ago | Created By-> /bin/sh -c #(nop)  ENV NODE_ENV=production | Size: 0B
ID-> f5be1883c8e0 | Created-> 2 years ago | Created By-> /bin/sh -c #(nop)  CMD ["node"] | Size: 0B

3. Exclude Files and Folders From the Image Build

Let's consider a situation where we have a large file, for example, a log, a zip, or a jar file. Or we might have files we don't want to expose in the final build, such as secret keys or passphrases.

We can use the .dockerignore file to avoid sending these files to the Docker server. It works similarly to the .gitignore file.

Suppose we have a file with a password in our project. We can create one, for example:

$ echo 'password' >secret.txt

Now, we want to exclude this file from our image. We can add it to our .dockerignore file:

# Ignoring the password file 
secret.txt

This way, we can exclude resources from the build context. Furthermore, following best practices, it improves performance. The image to upload will have a smaller size.

Also, we won't have cache invalidation issues of a new layer if, for example, we add a file to copy. We can also exclude folders with this approach:

# Ignore the logs directory
logs/

Let's build the image again with the .dockerignore file. Then, we can start a container:

$ docker run -d --publish 8000:8000 node-docker

Or, if we use Docker Compose, we can run docker-compose up with a docker-compose.yml file, for example:

services:
  node-app:
    image: node-docker:latest
    ports:
      - 8080:8080

We can double-check by executing the bash command in our container:

$ docker exec -it d8938bc93406 bash

Once inside the container, if we check the content, for example, with the ls command, no secret.txt file or other excluded resources must be in the running container.

4. Exclude Files and Folders With Docker Volumes

We've seen how we can exclude files and folders while building an image. We might want to do the same with running containers that use volumes.

One of the reasons could be to add files or folders without impacting what we have in our host.

Let's suppose that we now want to add a project folder to the Docker container. We could achieve it using a mount bind.

Let's make a Docker Compose example for that with our Nodejs application. Let's look at the docker-compose.yml file:

services:
  node-app:
    build: .
    ports:
      - 8081:8080
    volumes:
      - .:/app

We can run our container in the root directory of the project:

$ docker-compose up -d

The container starts, and as expected, if there are files or directories in the container to be mounted, Docker copies the contents into the volume.

Let's say that now, for some reason, we delete a file or a folder in our container. The same result happens in our host, and we'll lose those resources.

Let's see a couple of solutions to avoid deleting resources in our host.

4.1. Exclude Files

We can start by excluding the mount of a file in a container. Once we set the volume, we can do a workaround using the /dev/null command:

Again, let's see our docker-compose.yml file:

services:
  node-app:
    build: .
    ports:
      - 8080:8080
    volumes:
      - .:/app/
      - /dev/null:/app/secret.txt

When using /dev/null, we discard anything written to a file. In this case, we'll end up with secret.txt being empty. Moreover, there's no way to modify the file due to its bind to /dev/null.

4.2. Exclude Folders

More interestingly, we can exclude folders and sub-folders. We can achieve that by creating an anonymous or named volume over that particular directory or sub-directory.

Let's look at our YAML file:

services:
  node-app:
    build: .
    ports:
      - 8080:8080
    volumes:
      - .:/app
      - /app/node_modules/

The order here is relevant. First, we have our bind over the /app directory we created earlier. Then, we mount a volume for what we want to exclude, in this case, the /node_modules sub-directory.

If we were to persist our data, we could use a named volume:

volumes:
  - .:/app
  - my-vol:/app/node_modules/

volumes:
  my-vol:
    driver: local

Finally, we can try to modify the content of the /node_modules directory in the container. In this case, there will be no impact on our host.

5. Conclusion

In this article, we learned how to exclude resources like files or folders with Docker. We looked at examples starting from an image build to running containers. For containers, we saw examples using Docker Compose and the possibility of using volumes to exclude sub-folders.

As always, we can find working code examples over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!