Вы запустили Postgres в контейнере, добавили данные, потом удалили контейнер — и всё пропало. Это не баг, а нормальное поведение Docker. Разбираемся, почему так устроено и как правильно хранить данные, которые должны переживать перезапуски.
Почему данные в контейнере эфемерны
Контейнер — это изолированный процесс с собственной файловой системой. Эта файловая система создаётся из образа при старте контейнера, а при его удалении — исчезает вместе с ним.
Короткая формула: образ — неизменяемый шаблон, контейнер — временная рабочая копия.
Всё, что вы записали внутри работающего контейнера (строки в базе, загруженные файлы, логи), живёт на так называемом слое записи — тонком слое поверх образа. Docker удаляет этот слой вместе с контейнером командой docker rm.
docker run --name demo postgres:16
# ... создали таблицы, добавили строки ...
docker rm demo
# Данные исчезли
Для большинства случаев это даже удобно: запустил контейнер для теста, поиграл, удалил — чисто. Но базы данных, пользовательские файлы, состояние приложения должны жить дольше, чем один контейнер.
Три способа хранить данные вне контейнера
Docker предлагает три механизма: named volumes, bind mounts и tmpfs. У каждого своя область применения.
Named volumes — рекомендованный способ для данных
Том (named volume) — это каталог, которым управляет Docker. Он живёт на хосте в специальном месте (/var/lib/docker/volumes/) и не зависит от жизненного цикла контейнера.
# Создать том явно
docker volume create pgdata
# Подключить том к контейнеру
docker run -d \
--name postgres \
-v pgdata:/var/lib/postgresql/data \ # том:путь_внутри_контейнера
-e POSTGRES_PASSWORD=secret \
postgres:16
Теперь можно удалить контейнер, создать новый с тем же томом — данные никуда не денутся:
docker rm postgres
docker run -d \
--name postgres \
-v pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:16
# Все данные на месте
Управлять томами можно через docker volume:
docker volume ls # список томов
docker volume inspect pgdata # где лежит, когда создан
docker volume rm pgdata # удалить (только если не подключён)
docker volume prune # удалить все «ничейные» тома
Named volumes — правильный выбор для баз данных, файловых хранилищ, любых данных, которые нужны в продакшене.
Bind mounts — для разработки
Bind mount монтирует конкретный каталог или файл с хоста прямо в контейнер. Никакой «магии» Docker: вы сами говорите, какой путь на вашей машине куда попадёт внутри.
docker run -d \
--name app \
-v /Users/vva/projects/myapp:/app \ # хост:контейнер
eclipse-temurin:21-jre \
java -jar /app/app.jar
Главное применение — разработка: правите код в IDE, изменения сразу видны внутри контейнера без пересборки образа. Для Spring Boot это особенно удобно в паре с devtools.
# docker-compose.yml для разработки
services:
app:
image: eclipse-temurin:21-jre
volumes:
- ./build/libs:/app # собранный jar с хоста
command: java -jar /app/app.jar
Bind mounts не стоит использовать для хранения данных Postgres или другого состояния в продакшене: вы становитесь зависимым от структуры файловой системы конкретного хоста, а это ломает переносимость.
tmpfs — данные только в памяти
tmpfs монтирует каталог внутри контейнера в оперативную память хоста. Данные не уходят на диск и исчезают при остановке контейнера.
docker run --tmpfs /tmp myapp
Применяется для временных данных, которые не должны попасть на диск: чувствительные файлы (токены, ключи), кэши сессий, промежуточные вычисления. Недоступен на Windows-хостах.
Сравнение: когда что использовать
| Ситуация | Механизм |
|---|---|
| База данных в продакшене (Postgres, Redis) | Named volume |
| Файлы, загружаемые пользователями | Named volume |
| Код при локальной разработке | Bind mount |
| Конфигурационные файлы при разработке | Bind mount |
| Временные файлы, чувствительные данные | tmpfs |
Postgres в контейнере: полный пример
Запустим Postgres с именованным томом, чтобы данные сохранялись между перезапусками. Это типичная настройка для локальной разработки Spring Boot приложения.
# docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data # именованный том
volumes:
pgdata: # Docker создаст том автоматически
docker compose up -d
# Создали таблицы, добавили данные...
docker compose down # остановили и удалили контейнеры
docker compose up -d # подняли снова — данные на месте
Обратите внимание: docker compose down не удаляет тома. Для удаления тома нужен явный флаг:
docker compose down --volumes # удалить контейнеры И тома
Это защищает от случайной потери данных.
Куда смотреть, если данные пропали
Если после перезапуска контейнера данные исчезли — проверьте по шагам:
- Убедитесь, что использовался том, а не просто каталог внутри контейнера.
- Проверьте, что при остановке не применялся флаг
--volumes. - Убедитесь, что путь монтирования совпадает с тем, куда приложение реально пишет данные. У Postgres это
/var/lib/postgresql/data, у Redis —/data, у других — свои пути, смотрите в документации образа.
docker inspect postgres | grep -A 10 Mounts # проверить, что примонтировано
Коротко
- Данные внутри контейнера эфемерны: удалил контейнер — потерял данные.
- Named volume — правильный выбор для баз данных и любого состояния, которое должно жить дольше контейнера.
- Bind mount — монтирует каталог с хоста; подходит для разработки, не для продакшена.
- tmpfs — только в памяти, исчезает при остановке; для временных и чувствительных данных.
docker volume ls / inspect / rm / prune— основные команды управления томами.docker compose downне удаляет тома — нужен явный--volumes.
Что почитать дальше
- Запуск контейнеров — флаги
docker run, режимы работы, управление контейнерами. - Docker Compose — как описывать многоконтейнерные приложения и задавать тома в
docker-compose.yml. - Сетевое взаимодействие — как контейнеры общаются друг с другом и с хостом.