Docker Compose: volumes vs bind mounts — что использовать и когда
Разбор volumes и bind mounts в Docker Compose: различия, сценарии использования и типичные ошибки при работе с данными.
Как использовать
- Для кода и конфигов с хоста в разработке используйте bind mount: путь вида ./app:/app в volumes сервиса.
- Для данных БД и долгоживущих данных объявите named volume в секции volumes и монтируйте его в сервис (например db-data:/var/lib/postgresql/data).
- Не используйте абсолютные пути хоста для данных в общих Compose-файлах — теряется переносимость.
В Docker Compose данные в контейнер можно подключать двумя способами: volume (управляемый Docker) и bind mount (конкретный путь на хосте). Если перепутать сценарии — данные теряются при переносе проекта, либо код на хосте не попадает в контейнер при разработке. Проблема: для БД указали bind mount с путём вида /home/user/data — на другой машине или в CI этого каталога нет; или наоборот, для кода использовали volume и после каждого изменения приходится пересобирать образ. Симптомы: «пустой каталог» в контейнере, потеря данных после docker compose down, несовпадение файлов между хостом и контейнером. Ниже — когда использовать volume и когда bind mount, примеры для разработки и production, проверка монтирований и типичные ошибки по документации Docker Storage и Compose volumes.
Решение
Volume — хранилище, которым управляет Docker; данные переживают удаление контейнера, путь на хосте выбирает daemon. Bind mount — жёсткая привязка пути на хосте к пути в контейнере; хост и контейнер видят одни и те же файлы. В Compose тип задаётся форматом записи или long syntax с type: volume / type: bind.
Что такое volume и bind mount
- Volumes: managed by Docker daemon, retain data after container removal, подходят для БД и долгоживущих данных.
- Bind mounts: direct link между путём на хосте и контейнером, не изолированы Docker; удобны для кода и конфигов в разработке.
Ключевые различия:
| Критерий | Volume | Bind mount |
|---|---|---|
| Кто владеет путём | Docker | Вы (путь на хосте) |
| Переносимость | Да | Нет (путь хоста может отсутствовать) |
| Типичное использование | Данные БД, production | Код, конфиги с хоста, разработка |
Bind mount (локальная разработка)
Редактировать код на хосте и сразу видеть изменения в контейнере без пересборки образа.
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- ./app:/app
Путь относительный от каталога с Compose-файлом; на другой машине достаточно склонировать репозиторий.
Volume (production, данные БД)
Данные между перезапусками контейнеров, без привязки к пути на хосте.
services:
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Named volume db-data создаётся при первом docker compose up; при docker compose down данные сохраняются (если volume не удалять явно).
Комбинированный пример
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://app:secret@db:5432/appdb
volumes:
- ./src:/app/src
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Длинный синтаксис для явного типа:
volumes:
- type: bind
source: ./src
target: /app/src
- type: volume
source: db-data
target: /var/lib/postgresql/data
volume:
nocopy: true
Проверка
- Список volumes после запуска — убедиться, что named volume создан:
docker compose up -d
docker volume ls
В списке должен быть volume с префиксом имени проекта (или именем из секции volumes).
- Проверка монтирования в контейнер — путь внутри контейнера и что там лежит:
docker compose exec db ls -la /var/lib/postgresql/data
docker compose exec app ls -la /app/src
Для bind mount изменения в ./src на хосте сразу видны в /app/src в контейнере.
- Данные после пересоздания контейнера — пересоздать сервис и убедиться, что данные БД на месте:
docker compose down
docker compose up -d
# подключиться к БД и проверить наличие данных
Если использовали named volume, данные сохраняются.
Типичные ошибки
- Абсолютный путь хоста для данных в общем Compose-файле — на другой машине каталога нет, Compose не переносим. Для данных приложения и БД используйте named volume в секции
volumes, путь на хосте оставьте Docker. - Ожидание начального содержимого из образа в volume — при первом запуске с новым volume Docker может скопировать содержимое из образа в volume; для подпути или отключения копирования в long syntax есть
volume.nocopy: true. Для БД обычно оставляют по умолчанию, чтобы образ postgres мог проинициализировать каталог. - Bind mount для каталога, который на хосте пуст — контейнер увидит пустой каталог и может перезаписать нужные файлы из образа. Для кода это нормально (код с хоста); для данных приложения — лучше volume.
Где применять
- Разработка: bind mount для кода (./src, ./app) и опционально конфигов; один и тот же Compose на разных машинах при относительных путях.
- Production / CI: named volumes для БД и долгоживущих данных; не завязываться на абсолютные пути хоста.
- Один файл: комбинировать bind mount для кода и volume для данных в одном
docker-compose.yml.
Связанные сниппеты: Сеть между контейнерами (localhost), Разница docker run / start / exec.
Ссылки: Docker Storage, Compose volumes, Services — volumes.