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.