Docker - how can I copy a file from an image to a host?

DockerBoot2docker

Docker Problem Overview


My question is related to this question on copying files from containers to hosts; I have a Dockerfile that fetches dependencies, compiles a build artifact from source, and runs an executable. I also want to copy the build artifact (in my case it's a .zip produced by sbt dist in '../target/`, but I think this question also applies to jars, binaries, etc.

docker cp works on containers, not images; do I need to start a container just to get a file out of it? In a script, I tried running /bin/bash in interactive mode in the background, copying the file out, and then killing the container, but this seems kludgey. Is there a better way?

On the other hand, I would like to avoid unpacking a .tar file after running docker save $IMAGENAME just to get one file out (but that seems like the simplest, if slowest, option right now).

I would use docker volumes, e.g.:

docker run -v hostdir:out $IMAGENAME /bin/cp/../blah.zip /out

but I'm running boot2docker in OSX and I don't know how to directly write to my mac host filesystem (read-write volumes are mounting inside my boot2docker VM, which means I can't easily share a script to extract blah.zip from an image with others. Thoughts?

Docker Solutions


Solution 1 - Docker

To copy a file from an image, create a temporary container, copy the file from it and then delete it:

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id

Solution 2 - Docker

Unfortunately there doesn't seem to be a way to copy files directly from Docker images. You need to create a container first and then copy the file from the container.

However, if your image contains a cat command (and it will do in many cases), you can do it with a single command:

docker run --rm --entrypoint cat yourimage  /path/to/file > path/to/destination

If your image doesn't contain cat, simply create a container and use the docker cp command as suggested in Igor's answer.

Solution 3 - Docker

docker cp $(docker create --name temporary_copytainer registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key && docker rm temporary_copytainer

wanted to supply a one line solution based on pure docker functionality (no bash needed)

edit: container does not even has to be run in this solution

edit2: thanks to @Jonathan Dumaine for --rm so the container will be removed after, i just never tried, because it sounded illogical to copy something from somewhere which has been already removed by the previous command, but i tried it and it works

edit3: due the comments we found out --rm is not working as expected, it does not remove the container because it never runs, so I added functionality to delete the created container afterwards

Solution 4 - Docker

A much faster option is to copy the file from running container to a mounted volume:

docker run -v $PWD:/opt/mount --rm --entrypoint cp image:version /data/libraries.tgz /opt/mount/libraries.tgz

> real 0m0.446s

** VS **

docker run --rm --entrypoint cat image:version /data/libraries.tgz > libraries.tgz

> real 0m9.014s

Solution 5 - Docker

Parent comment already showed how to use cat. You could also use tar in a similar fashion:

docker run yourimage tar -c -C /my/directory subfolder | tar x

Solution 6 - Docker

Another (short) answer to this problem:

docker run -v $PWD:/opt/mount --rm -ti image:version bash -c "cp /source/file /opt/mount/"

Update - as noted by @Elytscha Smith this only works if your image has bash built in

Solution 7 - Docker

Not a direct answer to the question details, but in general, once you pulled an image, the image is stored on your system and so are all its files. Depending on the storage driver of the local Docker installation, these files can usually be found in /var/lib/docker/overlay2 (requires root access). overlay2 should be the most common storage driver nowadays, but the path may differ.

The layers associated with an image can be found using $ docker inspect image IMAGE_NAME:TAG, look for a GraphDriver attribute.
At least in my local environment, the following also works to quickly see all layers associated with an image:
docker inspect image IMAGE_NAME:TAG | jq ".[0].GraphDriver.Data"

In one of these diff directories, the wanted file can be found.
So in theory, there's no need to create a temporary container. Ofc this solution is pretty inconvenient.

Solution 8 - Docker

You essentially had the best solution already. Have the container copy out the files for you, and then remove itself when it's complete.

This will copy the files from /inside/container/ to your machine at /path/to/hostdir/.

docker run --rm -v /path/to/hostdir:/mnt/out "$IMAGENAME" /bin/cp -r /inside/container/ /mnt/out/

Solution 9 - Docker

I am using boot2docker on MacOS. I can assure you that scripts based on "docker cp" are portable. Because any command is relayed inside boot2docker but then the binary stream is relayed back to the docker command line client running on your mac. So write operations from the docker client are executed inside the server and written back to the executing client instance!

I am sharing a backup script for docker volumes with any docker container I provide and my backup scripts are tested both on linux and MacOS with boot2docker. The backups can be easily exchanged between platforms. Basically I am executing the following command inside my script:

docker run --name=bckp_for_volume --rm --volumes-from jenkins_jenkins_1 -v /Users/github/jenkins/backups:/backup busybox tar cf /backup/JenkinsBackup-2015-07-09-14-26-15.tar /jenkins

> Runs a new busybox container and mounts the volume of my jenkins container with the name jenkins_jenkins_1. The whole volume is written to the file backups/JenkinsBackup-2015-07-09-14-26-15.tar

I have already moved archives between the linux container and my mac container without any adjustments to the backup or restore script. If this is what you want you find the whole script an tutorial here: blacklabelops/jenkins

Solution 10 - Docker

You could bind a local path on the host to a path on the container, and then cp the desired file(s) to that path at the end of your script.

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

Then there is no need to copy afterwards.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMarkView Question on Stackoverflow
Solution 1 - DockerIgor BukanovView Answer on Stackoverflow
Solution 2 - DockerfonsView Answer on Stackoverflow
Solution 3 - DockerElytscha SmithView Answer on Stackoverflow
Solution 4 - DockerMikkoView Answer on Stackoverflow
Solution 5 - DockerwedesoftView Answer on Stackoverflow
Solution 6 - DockerfreedevView Answer on Stackoverflow
Solution 7 - DockernichoioView Answer on Stackoverflow
Solution 8 - DockerCameron HudsonView Answer on Stackoverflow
Solution 9 - DockerblacklabelopsView Answer on Stackoverflow
Solution 10 - DockerryanjdillonView Answer on Stackoverflow