Как скопировать файлы из Docker-контейнера на хост и обратно

Скопировать файл из контейнера в текущую папку:

docker cp app:/app/config.yml .

app — имя или ID контейнера из docker ps. Первый путь — внутри контейнера, второй — на вашем компьютере. Чтобы сразу переименовать файл, укажите новое имя: ./config.backup.yml. Если такой файл уже есть, Docker перезапишет его без вопросов.

Проверено на Docker 29.7 и Docker Compose 5.5 с контейнерами Alpine 3.24 и nginx:alpine.

Как узнать имя контейнера и путь к файлу

docker ps -a
docker exec app ls -l /app

docker ps -a показывает и остановленные контейнеры. Путь в контейнере всегда отсчитывается от корня: рабочую директорию docker cp не учитывает. Если WORKDIR контейнера — /app, то app:config.yml означает /config.yml. Полный путь к файлу из WORKDIR покажет docker exec app readlink -f config.yml.

Как скопировать папку из контейнера

# ./logs нет: Docker создаст её и положит туда файлы
docker cp app:/app/logs ./logs
# ./backup есть: получится ./backup/logs
docker cp app:/app/logs ./backup
# только содержимое: ./backup/a.log, ./backup/b.log
docker cp app:/app/logs/. ./backup

Если папки назначения нет, Docker создаёт её и кладёт туда содержимое исходной папки. Если она есть, исходная папка окажется внутри. Исключение — источник с /. на конце: тогда копируется только содержимое. Один слэш на конце (logs/) ничего не меняет.

Как скопировать файл с хоста в контейнер

Поменяйте аргументы местами:

docker cp ./settings.yml app:/app/
# получится /srv/static
docker cp ./static app:/srv
# содержимое static попадёт в /var/www
docker cp ./static/. app:/var/www

Правила для папок те же. Скопированный файл живёт только в этом контейнере: после docker rm или docker compose up --force-recreate его не будет. Файлы, без которых приложение не работает, кладите в образ (COPY в Dockerfile) или в том (volume).

Как скопировать файл внутри контейнера или между контейнерами

Скопировать файл внутри контейнера можно обычным cp:

docker exec app cp /app/config.yml /app/config.bak

Чтобы скопировать файлы между двумя контейнерами, передайте вывод одного docker cp в другой:

docker cp app:/app/logs - | docker cp - other:/tmp

Остановленный контейнер тоже подойдёт

docker cp работает с остановленными контейнерами в обе стороны. Если контейнер падает при старте, достаньте из него логи, положите исправленный конфиг и запустите docker start app. Файлы из томов тоже копируются.

docker compose cp

В папке с compose.yaml вместо имени контейнера пишите имя сервиса:

docker compose cp web:/app/config.yml .
docker compose cp ./override.yml web:/app/

Если у сервиса несколько реплик, при копировании в сервис файл попадёт в каждую. При копировании из сервиса Compose возьмёт файл у одной реплики, не обязательно у первой. Нужную выберите через --index:

docker compose cp --index 2 web:/app/config.yml .

С остановленными сервисами docker compose cp тоже работает.

Права и владелец файлов

Права доступа (rwx) сохраняются в обе стороны. Владелец сохраняется только при копировании в контейнер, и только как числа UID и GID.

Из контейнера на хост: файлы принадлежат тому, кто запустил docker cp. Поэтому после sudo docker cp в Linux файлы на хосте принадлежат root. Вернуть их себе:

sudo chown -R "$(id -u):$(id -g)" ./logs

Чтобы обходиться без sudo, добавьте себя в группу docker (sudo usermod -aG docker $USER) и перелогиньтесь, как с любой новой группой. Учтите: эта группа даёт на машине права root.

С хоста в контейнер: файлы сохраняют числовые UID и GID, которые были у них на хосте, и sudo docker cp их не меняет. У обычного пользователя Linux это 1000:1000, на Mac — 501:20. Если приложение в контейнере работает под другим пользователем, записать в такой файл оно не сможет: Permission denied. С флагом -a Docker отдаёт файлы пользователю контейнера, тому, что задан в --user или USER в Dockerfile:

docker cp -a ./settings.yml app:/app/

Если ни --user, ни USER не заданы, -a ничего не меняет. При копировании из контейнера флаг тоже ни на что не влияет. Поменять владельца потом: docker exec -u 0 app chown 1000:1000 /app/settings.yml.

Как достать файл из образа

docker cp работает только с контейнером, но запускать его не обязательно. docker create создаёт контейнер из образа и ничего не запускает:

docker create --name tmp nginx:alpine
docker cp tmp:/etc/nginx/nginx.conf .
docker rm tmp

Если у образа нет команды по умолчанию (FROM scratch), Docker ответит no command specified. Передайте любую команду, выполнять её никто не будет:

docker create --name tmp my-image none

Как передать tar-архив через -

Если вместо локального пути написать -, docker cp выдаст tar-архив в stdout или прочитает его из stdin:

docker cp app:/app/logs - > logs.tar
docker cp app:/app/logs - | gzip > logs.tar.gz
tar -c -C ./site . | docker cp - app:/var/www

При чтении из stdin папка назначения должна существовать, архив в gzip тоже подойдёт. На Mac tar добавляет в архив расширенные атрибуты, и Docker падает с ошибкой xattr "com.apple.provenance": operation not supported. Упакуйте без них:

COPYFILE_DISABLE=1 tar --no-xattrs -c -C ./site . | docker cp - app:/var/www

У обычного docker cp ./site/. app:/var/www такой проблемы нет.

Как скопировать файлы по маске или из tmpfs

Маски docker cp не раскрывает: docker cp 'app:/app/logs/*.log' . закончится ошибкой. В tmpfs он видит пустую папку, а /proc, /sys и /dev не читает вовсе. В таких случаях запустите tar в работающем контейнере:

mkdir -p logs scratch
docker exec app sh -c 'cd /app/logs && tar -c *.log' | tar -x -C ./logs
docker exec app tar -c -C /scratch . | tar -x -C ./scratch

docker exec здесь без -t: с терминалом архив придёт битым.

Как скопировать файл из контейнера в Windows

В PowerShell команда та же. Путь с буквой диска Docker считает локальным:

docker cp app:/app/config.yml C:\Users\me\Desktop

Частые ошибки

  • No such container: app — контейнера с таким именем или ID нет, сверьтесь с docker ps -a. Compose называет контейнеры вида project-web-1, а в docker compose cp нужно имя сервиса.
  • Could not find the file /app/config.yml in container app — неверный путь в контейнере. При копировании в контейнер ошибка значит, что нет папки, куда вы копируете: для app:/app/conf/settings.yml это /app/conf. Та же ошибка бывает с путями от WORKDIR, с масками и с файлами в tmpfs.
  • no such directory или invalid output path: directory "…" does not exist — нет папки назначения на хосте или в контейнере. Создайте её через mkdir -p, в контейнере — docker exec app mkdir -p /app/conf.
  • cannot copy directory — вы копируете папку поверх существующего файла.
  • copying between containers is not supported — в обоих аргументах есть двоеточие. Если двоеточие стоит в имени локального файла, допишите в начало ./: ./backup:1.yml.
  • must specify at least one container source — ни в одном аргументе нет контейнер:.
  • container rootfs is marked read-only — контейнер запущен с --read-only. В его тома копировать можно.
  • Вместо файла пришла битая ссылка: источник был симлинком, и Docker скопировал саму ссылку. С флагом -L он скопирует файл, на который она указывает.
Консультирую по продукту, собираю MVP и учу делать продукты самостоятельно. Поработать со мной →