DOCKER
#docker#docker-compose#volumes#storage

Docker Compose: volumes vs bind mounts — что использовать и когда

Разбор volumes и bind mounts в Docker Compose: различия, сценарии использования и типичные ошибки при работе с данными.

Как использовать

  1. Для кода и конфигов с хоста в разработке используйте bind mount: путь вида ./app:/app в volumes сервиса.
  2. Для данных БД и долгоживущих данных объявите named volume в секции volumes и монтируйте его в сервис (например db-data:/var/lib/postgresql/data).
  3. Не используйте абсолютные пути хоста для данных в общих 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; удобны для кода и конфигов в разработке.

Ключевые различия:

КритерийVolumeBind 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

Проверка

  1. Список volumes после запуска — убедиться, что named volume создан:
docker compose up -d
docker volume ls

В списке должен быть volume с префиксом имени проекта (или именем из секции volumes).

  1. Проверка монтирования в контейнер — путь внутри контейнера и что там лежит:
docker compose exec db ls -la /var/lib/postgresql/data
docker compose exec app ls -la /app/src

Для bind mount изменения в ./src на хосте сразу видны в /app/src в контейнере.

  1. Данные после пересоздания контейнера — пересоздать сервис и убедиться, что данные БД на месте:
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.