Volumes и bind mounts в Docker: как сохранять данные контейнеров
Практика3 минРедакция Innovialabs
Коротко
Всё, что записано в слой контейнера, исчезает при его удалении. Для данных приложения используйте named volumes, для исходного кода при разработке — bind mounts, а бэкап делайте отдельно и проверяйте восстановление.
Почему данные в контейнере теряются
Каждый контейнер получает поверх образа тонкий записываемый слой. Всё, что приложение пишет на диск без подключённого хранилища, попадает туда. Остановка контейнера данные не удаляет, но при docker rm, пересоздании через Compose или обновлении образа этот слой исчезает вместе с файлами.
Поэтому правило простое: всё, что должно пережить контейнер — базы данных, загруженные файлы, очереди, — хранится вне записываемого слоя.
Три варианта хранения
| Тип | Где лежат данные | Для чего подходит |
|---|---|---|
| Named volume | В области, которой управляет Docker | Базы данных, загрузки, продакшен |
| Bind mount | В конкретной папке на хосте | Исходный код при разработке, конфиги |
| tmpfs | В оперативной памяти | Временные и чувствительные данные, которые не нужно сохранять |
Named volumes
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16
Почему volumes — выбор по умолчанию для данных:
- Docker сам создаёт и хранит их, путь на хосте знать не нужно.
- Работают одинаково на Linux, macOS и Windows.
- Если подключить пустой volume в папку, где в образе уже есть файлы, Docker скопирует их туда при первом использовании.
- Легко переносятся между контейнерами и переживают их удаление.
Полезные команды: docker volume ls, docker volume inspect pgdata, docker volume rm pgdata. Осторожно с docker volume prune — он удаляет неиспользуемые volumes вместе с данными.
Bind mounts
docker run -d -v "$(pwd)/src:/app/src" my-app
Папка хоста подключается в контейнер напрямую: изменения видны сразу в обе стороны. Это удобно для разработки — правите код в редакторе, приложение в контейнере видит изменения.
Минусы:
- Зависимость от структуры папок конкретной машины.
- Контейнер может изменить или удалить файлы на хосте.
- На macOS и Windows файловые операции через bind mount могут работать медленнее.
Для конфигов, которые контейнер только читает, добавляйте :ro — монтирование только для чтения.
Проблемы с правами доступа
Самая частая ошибка — Permission denied. Причина: внутри контейнера процесс работает от пользователя с одним UID, а файлы на хосте принадлежат другому UID. Имена пользователей не важны — сравниваются числовые идентификаторы.
Как решать:
- Узнайте UID процесса в контейнере:
docker exec <container> id. - Выставьте владельца папки на хосте под этот UID через
chown. - Для разработки можно запускать контейнер с вашим UID:
--user "$(id -u):$(id -g)". - В своём Dockerfile создавайте пользователя и папки данных заранее с правильным владельцем.
- Не решайте проблему через
chmod 777— это открывает файлы всем.
Как сделать бэкап volume
Универсальный способ — временный контейнер, который архивирует содержимое volume в папку хоста:
docker run --rm \
-v pgdata:/data:ro \
-v "$(pwd)":/backup \
alpine tar czf /backup/pgdata.tar.gz -C /data .
Восстановление в новый volume:
docker run --rm \
-v pgdata_restored:/data \
-v "$(pwd)":/backup \
alpine tar xzf /backup/pgdata.tar.gz -C /data
Важно для баз данных: копия файлов работающей базы может оказаться несогласованной. Для них надёжнее штатные инструменты — pg_dump, mysqldump — или остановка контейнера на время копирования. И главное — регулярно проверяйте, что из бэкапа действительно можно восстановиться.
Как выбрать
- Данные, которые нужно сохранить в продакшене, — named volume.
- Код и конфиги при локальной разработке — bind mount.
- Временные файлы, кэш, секреты в памяти — tmpfs.
- Ничего важного — в записываемом слое контейнера.