Docker Compose: volumes vs bind mounts — что использовать и когда
Разбор volumes и bind mounts в Docker Compose: различия, сценарии использования и типичные ошибки при работе с данными.
Как использовать
- Скопируйте нужный фрагмент кода.
- Вставьте в свой проект и при необходимости измените под задачу.
- Проверьте зависимости и окружение (версии, переменные).
Различия между volume и bind mount в Docker Compose: когда что использовать и как не наступить на типичные грабли. Ниже — строго по официальной документации Docker Storage и Compose File (volumes).
Что такое volume и bind mount
Volume — хранилище данных, которым управляет Docker (daemon). Данные лежат в каталоге на хосте, но путь выбирает Docker; к ним обращаются только через монтирование в контейнер. Volume переживает удаление контейнера и подходит для долгоживущих данных.
Bind mount — привязка конкретного пути на хосте к пути внутри контейнера. Контейнер видит ровно то, что лежит по этому пути на хосте; хост и контейнер могут одновременно изменять одни и те же файлы.
- Volumes: «managed by the Docker daemon», «retain data even after the containers using them are removed», «ideal for performance-critical data processing and long-term storage».
- Bind mounts: «direct link between a host system path and a container», «aren’t isolated by Docker», «both non-Docker processes on the host and container processes can modify the mounted files simultaneously».
Ключевые различия
| Критерий | Volume | Bind mount |
|---|---|---|
| Кто владеет путём | Docker (путь на хосте выбирает daemon) | Вы (указываете путь на хосте) |
| Переносимость | Да: один и тот же Compose-файл на любой машине | Нет: путь хоста может не существовать или отличаться |
| Создание каталога | Docker создаёт volume при первом использовании | Compose/legacy создаёт каталог по пути, если его нет (можно отключить long syntax) |
| Типичное использование | Данные приложения, БД, production | Локальная разработка: код, конфиги с хоста |
В Compose в секции сервиса можно указать оба типа через атрибут volumes; тип задаётся либо коротким синтаксисом (по формату пути/имени), либо длинным с type: volume или type: bind. См. Services — volumes.
Когда использовать bind mount (локальная разработка)
Bind mount уместен, когда нужно:
- редактировать код на хосте и сразу видеть изменения в контейнере (без пересборки образа);
- подмонтировать конфиг или один файл с хоста;
- отладить приложение, подставляя файлы с хоста.
Из документации: «Use bind mounts when you need to be able to access files from both the container and the host.»
Пример: приложение в контейнере, код лежит в ./app на хосте, в контейнере он должен быть в /app.
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- ./app:/app
Любое изменение в ./app на хосте сразу видно в контейнере. Путь относительный (от каталога с Compose-файлом); на другой машине достаточно склонировать репозиторий — структура каталогов та же.
Когда использовать volume (production)
Volume уместен, когда нужно:
- хранить данные БД, кэши, загрузки и т.п. между перезапусками контейнеров;
- не привязываться к конкретному пути на хосте (деплой на разные серверы, CI);
- чтобы данными управлял Docker (создание, бэкапы через
docker run -vи т.д.).
Из документации: volumes «retain data even after the containers using them are removed» и подходят для «long-term storage».
Пример: PostgreSQL хранит данные в named volume db-data; при docker compose down данные не пропадают (если volume не удалён отдельно).
services:
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
# порты, healthcheck и т.д.
volumes:
db-data:
Имя db-data в секции volumes объявляет named volume; Compose создаёт его при первом docker compose up, если его ещё нет. См. Define and manage volumes in Docker Compose.
Пример docker-compose.yml для обоих вариантов
Один файл: приложение (код с хоста через bind mount) и БД (данные в volume).
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://app:secret@db:5432/appdb
volumes:
# bind mount: код с хоста для разработки
- ./src:/app/src
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
# named volume: данные БД, не привязаны к пути на хосте
- db-data:/var/lib/postgresql/data
volumes:
db-data:
- Разработка: меняешь файлы в
./src— контейнерappвидит изменения без пересборки. - Данные БД: лежат в volume
db-data; при пересоздании контейнераdbданные сохраняются.
Длинный синтаксис для явного указания типа (из Services — volumes):
volumes:
- type: bind
source: ./src
target: /app/src
- type: volume
source: db-data
target: /var/lib/postgresql/data
volume:
nocopy: true
Для одного сервиса можно комбинировать оба типа в одном списке volumes.
Типичная ошибка новичков
Ошибка: Использовать bind mount с жёстко прописанным абсолютным путём хоста для данных приложения или БД в Compose-файле, который потом используют на другой машине или в CI.
Пример плохо:
volumes:
- /home/developer/myproject/data:/var/lib/postgresql/data
На другой машине каталога /home/developer/myproject/data может не быть, либо там другие данные — композ перестаёт быть переносимым.
Правильно: для данных, которые должны жить между запусками и не зависеть от пути на хосте, объявлять named volume в секции volumes и монтировать его в сервис. Путь на хосте остаётся заботой Docker.
Ещё одна ошибка: ожидать, что при первом запуске с volume в каталоге volume уже лежат файлы из образа (например, инициализация БД). По умолчанию при создании volume Docker может копировать начальное содержимое из образа в volume; если нужно монтировать только подпуть или отключить копирование, в long syntax используется опция volume.nocopy: true. Для БД обычно оставляют поведение по умолчанию (без nocopy), чтобы образ postgres мог проинициализировать каталог при первом запуске.
Резюме: для кода и конфигов с хоста — bind mount (как правило, относительный путь ./...). Для постоянных данных приложения и БД — named volume в Compose.
Кратко
- Volume — управляется Docker, путь на хосте не важен, данные переживают контейнер; использовать для БД и долгоживущих данных, в т.ч. в production.
- Bind mount — жёсткая связь пути на хосте и в контейнере; использовать для кода и конфигов в разработке, когда нужен доступ с обеих сторон.
- В одном
docker-compose.ymlможно комбинировать: bind mount для кода, named volume для данных. - Не завязывать production/общие сценарии на абсолютные пути хоста в bind mount; для данных использовать named volume.
Ссылки на документацию: