58

I want to get some files when dockerfile built successfully, but it doesn't copy files from container to host.

That means when I have built dockerfile, the files already be host.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
Allen
  • 583
  • 1
  • 4
  • 7
  • [Docker - copy file from container to host](https://stackoverflow.com/q/22049212/6521116) – LF00 Nov 17 '17 at 06:16

6 Answers6

75

This is now possible since Docker 19.03.0 in July 2019 introduced "custom build outputs". See the official docs about custom build outputs.

To enable custom build outputs from the build image into the host during the build process, you need to activate the BuildKit which is a newer recommended back-compatible way for the engine to do the build phase. See the official docs for enabling BuildKit.

This can be done in 2 ways:

  1. Set the environment variable DOCKER_BUILDKIT=1, or
  2. Set it in the docker engine by default by adding "features": { "buildkit": true } to the root of the config json.

From the official docs about custom build outputs:

custom exporters allow you to export the build artifacts as files on the local filesystem instead of a Docker image, which can be useful for generating local binaries, code generation etc.

...

The local exporter writes the resulting build files to a directory on the client side. The tar exporter is similar but writes the files as a single tarball (.tar).

If no type is specified, the value defaults to the output directory of the local exporter.

...

The --output option exports all files from the target stage. A common pattern for exporting only specific files is to do multi-stage builds and to copy the desired files to a new scratch stage with COPY --from.

e.g. an example Dockerfile

FROM alpine:latest AS stage1
WORKDIR /app
RUN echo "hello world" > output.txt

FROM scratch AS export-stage
COPY --from=stage1 /app/output.txt .

Running

DOCKER_BUILDKIT=1 docker build --file Dockerfile --output out .

The tail of the output is:

 => [export-stage 1/1] COPY --from=stage1 /app/output.txt .
0.0s
 => exporting to client
0.1s
 => => copying files 45B
0.1s

This produces a local file out/output.txt that was created by the RUN command.

$ cat out/output.txt
hello world

All files are output from the target stage

The --output option will export all files from the target stage. So using a non-scratch stage with COPY --from will cause extraneous files to be copied to the output. The recommendation is to use a scratch stage with COPY --from.


Windows is not supported for now:

malat
  • 12,152
  • 13
  • 89
  • 158
Ben T
  • 4,656
  • 3
  • 22
  • 22
  • this looked promising but for some reason I get files from different stages copied too. Is there any way to just get the output from this specific stage and not the others? – Vincent Gerris Jul 02 '20 at 10:51
  • The `--from` flag is used to specify the stage to copy from. – Ben T Aug 31 '20 at 00:22
  • 4
    In 2020 this should be the selected answer! This "IS" the way. Ben T => Thanks! – Xavi Montero Oct 17 '20 at 11:58
  • In 2021, it still works – micfan Sep 28 '21 at 19:35
  • its still working – PHP Avenger Dec 14 '21 at 19:54
  • NOTE: The `scratch` container is a "empty" container image built into Docker. I am guessing that is why you don't get a bunch of operating system files. – Vaccano Jan 14 '22 at 20:58
  • 1
    How to only get the exact file, not all of the file in `/`? @Vaccano @Ben T – gfan Mar 02 '22 at 10:49
  • @gfan The example above already copies a single file. What are you seeing? – Ben T Mar 03 '22 at 07:00
  • I tried to use different images, like `FROM scratch ` or `FROM alpine`, If use alpine, it will copy all the file under `/`, but use scratch, it only copy the `output.txt` – gfan Mar 03 '22 at 11:52
  • 1
    @gfan This is the documented behavior. The docs state `The --output option exports **all** files from the target stage.` – Ben T Mar 16 '22 at 11:04
  • Is there any way to specify "output" in a compose.yaml? So that a `docker compose build` will create my target file? – Skeeve Aug 07 '23 at 07:34
20

Copying files "from the Dockerfile" to the host is not supported. The Dockerfile is just a recipe specifying how to build an image.

When you build, you have the opportunity to copy files from host to the image you are building (with the COPY directive or ADD)

You can also copy files from a container (an image that has been docker run'd) to the host with docker cp (atually, the cp can copy from the host to the container as well)

If you want to get back to your host some files that might have been generated during the build (like for example calling a script that generates ssl), you can run a container, mounting a folder from your host and executing cp commands.

See for example this getcrt script.

docker run -u root --entrypoint=/bin/sh --rm -i -v ${HOME}/b2d/apache:/apache apache << COMMANDS
pwd
cp crt /apache
cp key /apache
echo Changing owner from \$(id -u):\$(id -g) to $(id -u):$(id -u)
chown -R $(id -u):$(id -u) /apache/crt
chown -R $(id -u):$(id -u) /apache/key
COMMANDS

Everything between COMMANDS are commands executed on the container, including cp ones which are copying on the host ${HOME}/b2d/apache folder, mounted within the container as /apache with -v ${HOME}/b2d/apache:/apache.

That means each time you copy anything on /apache in the container, you are actually copying in ${HOME}/b2d/apache on the host!

Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    Thanks for your reply. So particular, I will have a try. – Allen Oct 27 '15 at 21:18
  • How can we achieve this from dockerfile ?? – SudarP Sep 23 '19 at 03:56
  • @SudarP I thought this 2015 answer was to say it could not be done? What is your specific use case for this? – VonC Sep 23 '19 at 06:40
  • 1
    There could be use cases where you need to copy back to the host explicitly during build time, not during a later container run time. For example, suppose you have a multi-stage build where within one build stage you execute some unit tests and write a test coverage report. You need this to pass during specifically the build phase - failed tests should fail the build, and not occur in a later container run. But you might also need to retrain the code coverage report files for other uses outside of any image or container. – ely Sep 22 '20 at 16:39
  • 1
    On top of this, the whole process could be running in a CI/CD system where it is expensive to pull the Docker container and run it in a whole new CI/CD step just for purposes of copying out a file - you truly need it to happen within the same CI/CD process where the build is happening, not any later container pull + run. – ely Sep 22 '20 at 16:40
  • @ely Good points. Multi-stage builds did not exist when I wrote this answer. – VonC Sep 22 '20 at 21:44
9

Although it's not directly supported via the Dockerfile functionality, you can copy files from a built docker image.

containerId=$(docker create example:latest)
docker cp "$containerId":/source/path /destination/path
docker rm "$containerId"
Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
1

Perhaps you could mount a folder from the host as a volume and put the files there? Usually mounted volumes could be written to from within the container, unless you specify read-only option while mounting.

docker run -v ${PWD}:/<somewritablepath> -w <somewritablepath> <image> <action>
0

On completion of Docker Build using Dockerfile, following worked for me :

where /var/lib/docker/ is Docker Root Dir: at my setup.

sudo find /var/lib/docker/ -name DESIRED_FILE -type f | xargs sudo ls -hrt | tail -1 | grep tgz | xargs  -i  sudo cp -v  '{}' PATH_ON_HOST
Shivu RH
  • 21
  • 4
0

You can also use the network. Starting with the script from this answer: https://stackoverflow.com/a/58255859/836450

  • I run this script on my host in the folder I want my output to go

  • Inside my docker I have:

    RUN apt-get install -y curl RUN curl -F 'file=@/path/to/file' http://172.17.0.1:44444/

Note, for me at least, 172.17.0.1 is always the IP of the host while building the dockerfile

benathon
  • 7,455
  • 2
  • 41
  • 70