91

Is there a command/option to display or list the context which is sent to the Docker daemon for building an image?

$ docker build -t "image-name"
Sending build context to Docker daemon 8.499 MB
...

Files and directories can be excluded from the build context by specifying patterns in a .dockerignore file. I guess what I'm looking for amounts to testing the .dockerignore in addition to any other niche rules Docker uses when determined the context.

Steve
  • 1,618
  • 3
  • 17
  • 30

6 Answers6

78

Answers above are great, but there is a low-tech solution for most cases - ncdu. This utility will show pretty and interactive tree structure with sizes. It has an option that will take patterns from a file and exclude them from scan. So you can just do ncdu -X .dockerignore. You will get something like this:

enter image description here

This is pretty close to what you will get in your docker image. One caveat is thou if you add a dot directory (like .yarn) into an image, it will not show in ncdu output.

RawCode
  • 1,073
  • 8
  • 13
65

The only way would be to add the current directory to an specific directory and list it.

Try building with this Dockerfile:

FROM busybox

RUN mkdir /tmp/build/
# Add context to /tmp/build/
COPY . /tmp/build/

Build it with:

docker build -t test .

List all the files and directories in /tmp/build:

docker run --rm -it test find /tmp/build
Ricardo Branco
  • 5,740
  • 1
  • 21
  • 31
  • 25
    You could do it in one step by including `RUN find /tmp/build` in the Dockerfile. – tlrobinson Jan 21 '19 at 20:59
  • Sorry, I don't know about Windows but WSL should have these GNU utilities installed. – Ricardo Branco Jun 03 '20 at 09:35
  • 1
    You no longer need `RUN mkdir` – jchook Mar 17 '22 at 02:32
  • grabbing the one-liners approach from @whitone, but adapting to use the commands from this answer, for when buildx isn't available, we get: `printf 'FROM busybox\nCOPY . /tmp/build/\nRUN find /tmp/build' | docker build -f- .` – Tom Saleeba Jul 19 '23 at 07:26
  • ...or if you want to see the size of the directories to help focus your search for large objects, you can use `printf 'FROM busybox\nCOPY . /tmp/build/\nRUN du -xhd1 /tmp/build' | docker build -f- .` (`du -xhd1` is the difference) – Tom Saleeba Jul 19 '23 at 07:31
33

Starting with version 18.09, Docker has an option to export context data using BuildKit backend.

It's not enabled by default, so you need to set an environment variable DOCKER_BUILDKIT=1 before invoking docker build command.

The following command can work also if you don't have any Dockerfile in current directory.

printf 'FROM scratch\nCOPY . /' | DOCKER_BUILDKIT=1 docker build -f- -o context .

When you run multiple times remember to delete previous export with rm -r context.

You can also get context data as archive and then mount with archivemount command:

printf 'FROM scratch\nCOPY . /' | DOCKER_BUILDKIT=1 docker build -f- -o- . > context.tar
mkdir context
archivemount context.tar context

With both methods, then you can explore the result with ncdu context.

whitone
  • 341
  • 3
  • 3
  • 2
    nice, great answer ️(for people not wanting to install ncdu: `du -m context | sort -n`) ‍♂️️ – Herbert Poul Jun 26 '20 at 09:35
  • 1
    Instead of redirecting to `context.tar` you also can just pipe the output to `tar -tv` so you don't have to deal with temporary files and directories. – David Ongaro Aug 15 '20 at 01:32
13

Updated answer: Since 2017, Docker has recommended to use COPY instead of ADD and with the comment from @tlrobinson, the simpler Dockerfile looks like so:

# debug and list the docker build context so that you can minimmize it
#
# usage:
#  docker build -f docker/context.Dockerfile -t test/buildcontext .
#
######################
FROM busybox

RUN mkdir /tmp/build/
# Add context to /tmp/build/
COPY . /tmp/build

# this last command outputs the list of files added to the build context:
RUN find /tmp/build/

Jesper Rønn-Jensen
  • 106,591
  • 44
  • 118
  • 155
  • 1
    I updated my answer to use `COPY` but the `find` in `RUN` doesn't have the flexibility of using the `find` command line options unless one uses ENTRYPOINT. – Ricardo Branco Feb 18 '19 at 10:00
1

What worked for me is to do the following (based on this article).

  1. Tell Docker to use the old build kit. In PowerShell that is:

    $ENV:DOCKER_BUILDKIT=0

  2. Run Docker build so that it reports ALL the progress it's making:

    docker build --progress=plain BLAH

Given those two things you can then do something as simple as this in your Docker file:

RUN ls /app

And that will give you a list out of everything in the /app folder.

Louis Cribbins
  • 121
  • 1
  • 2
  • `RUN ls -R` is a better option, it's recursive, also run `docker build --no-cache` whilst tweaking your .gitignore file. – alv Jan 18 '23 at 12:30
1

Here is a script that outputs the context tarball sent by docker build to the Docker daemon. You can examine it like /path/to/script_below | tar -tv for example.

#!/usr/bin/python3

from http.server import BaseHTTPRequestHandler
from re import match
from socketserver import UnixStreamServer
from subprocess import Popen, DEVNULL
from sys import exit, stdout
from tempfile import TemporaryDirectory

class ContextDumper(BaseHTTPRequestHandler):
    def __init__(self, request, client_address, server):
        # provide dummy client address for error reporting
        super().__init__(request, ('docker_context', 0), server)
        
    def do_HEAD(self):
        if self.path != '/_ping':
            self.send_error(404, '/_ping only')

    do_GET = do_HEAD

    def do_POST(self):
        if match(r'/v1\.\d+/build\?', self.path) is None:
            self.send_error(404, '/v1.X/build only')
            exit(1)
        total = 0
        while True:
            chunk_length = int(self.rfile.readline().strip(), 16)
            total += chunk_length
            chunk = self.rfile.read(chunk_length)
            stdout.buffer.write(chunk)
            self.rfile.readline()
            if chunk_length == 0:
                break
        padding = (512 - (total % 512)) % 512
        stdout.buffer.write(bytes(padding))
        exit(0)

with TemporaryDirectory() as tmpdir:
    socket_path = tmpdir + '/docker_context.sock'
    fake_dockerd = UnixStreamServer(socket_path, ContextDumper)
    with Popen(['docker', f'--host=unix://{socket_path}', 'build', '--file=/dev/null', '.'],
               stdout=DEVNULL, stderr=DEVNULL) as builder:
        fake_dockerd.serve_forever()
Ferenc Wágner
  • 246
  • 2
  • 9