Copying files between host and Docker container

Added over 1 year ago by dave Vote >> (0) Up | Down | Flag

I've been working with docker quite a bit recently and I've run into a couple times where I needed to either access information in the running docker container from the host or move information from the host to the docker container. I was using git to commit things on one side and then pull them down on the other side, but it turns out there's a much simpler solution!

TL;DR

docker cp <local file path and name> <container id>:<file location in the volume>
docker cp <container id>:<file location in the volume> <local file path and name>

Some Background

There are 4 core concepts in Docker to understand:

  1. Images
  2. Containers
  3. Volumes
  4. Networks

Obviously there's more to it than this, but if you grasp these 4 concepts you'll be in a much better place to go deeper in any area you need.

Images

An image is what's created when you run the docker build command or docker compose up using a Dockerfile or the details in the docker-compose.yml file. An image is a snapshot of the build steps and data of a container. That's not quite the right way to describe it, but it's about as close as I can get today. Images are how you "move" containers around between machines, how you would deploy your code to a new machine without having to work through the build process on the new machine. An image doesn't run so you don't actually interact with it other than building it, moving it, and spinning up containers from it.

Containers

This is the heart of what you think about when you're using docker. A container is created from an image, and it does contain running code. Most of the containers that I use are based on either Alpine or Ubuntu, so they have their own operating system and programs that run on top of it, like nginx or sqlitebrowser. The containers have exposed ports so that you can access the running web server or program.

For example, I run a Node.js RESTful API in one container and a Next.js app in a different container. The Next.js app pulls information from the Node.js API using the exposed ports and sends information back in the same way. I have a third container for sqlitebrowser that shares a volume with the API container so that it can access the database. The application sqlitebrowser is a running application in the alpine os in the container, and it is exposed to the web through kasmvnc in the container. This allows me to directly interact with the database on the fly for functionality that wasn't implemented in the API.

There are a few things you can do with containers: create them (from images), start them, access them (through the exposed ports or through a docker exec command), stop them, and destroy them. There's probably other things but those are the core of what I do with containers.

Volumes

I mentioned volumes a minute ago. Think of volumes as a hard drive for a container. When you create and start a container all the information exists within the container. If that container doesn't have a volume when you destroy that container any data that changed while the container was running will also be destroyed. A volume is a more permanent storage place for data that might change while a container is running, like a database file for sqlite or uploads from a web browser. When you destroy the container the container contents are destroyed, but the volume is left alone, so if you create a new container from the image it can attach to the same image and all of the data in the volume persists. It's also important to note that volumes can be shared between containers, like in the sqlitebrowser example above, so that you're working against the same data from both containers.

Networks

Networks are how the containers can talk to each other without routing through the host. They can route through the host, but it's often easier to allow the containers to have direct contact with each other. You can do this through container names or IP addresses. I'm still learning networks, but one of the things you can do in a complex install is have multiple networks with each container only having access to the ones that they need. In the example above we can put the API and sqlitebrowser on the same network, and also a postgresql or redis container (or any other) if we want. And then we can put the Next.js app and the API on a different network, and this way the only way the Next.js app can access information in the postgresql or database containers is to use the API, so no direct access to the database. This helps minimize attack vectors on your network and encourages developers to adhere to the single responsibility and least access protocols.

The Heart of This Post

So, now that you have a better understanding of the concepts within docker, the last thing to touch on is that Docker always runs on a machine, called a host, and any of the running docker containers are called guests. Once a container is created sometimes you want to push new data to it, for example an updated sqlite database (which is a file) or perhaps a new module of code. It turns out that you can simply copy the file from the host to the container. The command for that looks like this:

docker cp <local file path and name> <container id>:<file location in the volume>

I find that often an example with placeholders is difficult to read, so let me give you a more concrete example. To find your container id you can run the docker ps command and then copy out the container id (it will look something like 47572b8c2fc7). You only need to grab enough digits from the front of the id to be unique, I find that 3 digits tends to be enough in an environment with less than 10 containers. Then let's say we want to copy a local file ./README.md to the /app/docs path on the running container. The command for that would look like:

docker cp ./README.md 475:/app/docs

If you want to go the other way and copy that file back from the container to the host the format looks like this:

docker cp <container id>:<file location in the volume> <local file path and name>

And filled in that would look like:

docker cp 475:/app/docs/README.md ./

Note: This command is run from the host machine, not the running container!

Hope that helps you in your docker journey. Please upvote if this post was useful, and I'm always open to any discussions in the comments! Have a great day!

Anonymous comments not allowed. Please login or create an account to leave a comment.

0 Comments