dockerignore in Docker images

Purpose

The purpose of this post is to learn how to use .dockerignore file in order to build smaller and more secure Docker images. These are some possible scenarios:

  • Docker daemon is on a remote machine and sending the build context is too slow.
  • We don’t want to build an image with passwords in it and Docker should ignore them.
  • Build smaller Docker images: Log files and other non-application related files are too heavy making the Docker image size too big.
  • Tell Docker to ignore files and folders that you don’t want in the final image.

Without using a .dockerignore file

Folder structure example:

$ tree -a
.
├── Dockerfile
├── logs
│   └── example.log
├── MY_SECRET_PASSWORDS.txt
└── src
    └── app.py

Dockerfile:

FROM alpine:3.7

ADD . /var/opt/

CMD sleep infinity

Command to build the image:

$ docker build -t testing .

Get current image size:

$ docker images | grep testing
testing   latest    5f1dbed20708    2 seconds ago   154MB

Container content in /var/opt/:

$ docker run -it testing ls -la /var/opt
total 24
drwxr-xr-x    4 root     root          4096 Apr 25 05:45 .
drwxr-xr-x   12 root     root          4096 Apr 25 05:45 ..
-rw-rw-r--    1 root     root            52 Apr 25 05:30 Dockerfile
-rw-rw-r--    1 root     root             0 Apr 25 05:27 MY_SECRET_PASSWORDS.txt
-rw-rw-r--    1 root     root             8 Apr 25 05:39 dockerignore
drwxrwxr-x    2 root     root          4096 Apr 25 05:27 logs
drwxrwxr-x    2 root     root          4096 Apr 25 05:31 src

Using a .dockerignore file

We want to reduce the Docker image size preventing to upload 150MB of logs and avoiding to upload passwords (or other unnecessary files) to build a more secure artifact.

touch .dockerignore

.dockerignore example file, preventing to upload logs, passwords and .dockerignore itself:

logs/
MY_SECRET_PASSWORDS.txt
.dockerignore

We can use UNIX glob patterns in a Docker ignore file. For example to ignore all files starting by bkp*, ending with *.md or ignore all compiled Python files in all folders recursively **/*.pyc.

Result:

$ docker build -t testing .
$ docker run -it testing ls -la /var/opt
total 16
drwxr-xr-x    3 root     root          4096 Apr 25 05:35 .
drwxr-xr-x   12 root     root          4096 Apr 25 05:35 ..
-rw-rw-r--    1 root     root            52 Apr 25 05:30 Dockerfile
drwxrwxr-x    2 root     root          4096 Apr 25 05:31 src


But we can do a better .dockerignore file disallowing everything and just allowing src folder like:

$ cat .dockerignore
**
!src

Container content in /var/opt:

$ docker build -t testing .
$ docker run -it testing ls -la /var/opt
total 12
drwxr-xr-x    3 root     root          4096 Apr 25 05:39 .
drwxr-xr-x   12 root     root          4096 Apr 25 05:39 ..
drwxrwxr-x    2 root     root          4096 Apr 25 05:31 src

Final image size:

$ docker images | grep testing          
testing   latest    b14a907cb76d    12 minutes ago    4.15MB

More info about .dockerignore syntax here: https://docs.docker.com/engine/reference/builder/#dockerignore-file

Bonus track: .dockerignore is not .gitignore

.dockerignore should be placed in the root of the Docker context, otherwise doesn’t work because Docker doesn’t allow us to have multiple recursive .dockerignore files:

https://github.com/moby/moby/issues/20944

Using previous example command:

$ docker build -t testing .

.dockerignore must be in the current folder . like ./dockerignore

If you have a Dockerfile in a subfolder for example ./build/Dockerfile you can still build the image specifying where is the Dockerfile and which is the context:

$ docker build -t testing -f build/Dockerfile .

As you can see .dockerignore should be placed in ./dockerignore, never in build/.dockerignore.