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 build context is too slow.
  • We don’t want to build an image with passwords in it.
  • Log files and other non-application related files are too heavy making the image size too big.

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 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

Example of .dockerignore, preventing to upoload logs, passwords and .dockerignore itself:

logs/
MY_SECRET_PASSWORDS.txt
.dockerignore

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.