Docker удобен тем, что быстро поднимает окружения, изолирует приложения и предсказуемо разворачивает сервисы на Linux VDS. Но у этой модели есть обратная сторона: данные контейнеров, слои образов, кэш сборок и логи постепенно растут в одном месте — чаще всего в каталоге /var/lib/docker. В какой-то момент на сервере заканчивается место, база перестаёт писать, деплой падает, а команда df -h показывает тревожные 100%.
Главное правило: не удаляйте файлы внутри /var/lib/docker вручную, если точно не понимаете, что делаете. Docker ведёт собственную базу метаданных, связывает контейнеры со слоями overlay2, volumes и сетями. Удаление «тяжёлой папки» через rm может привести к неработающим контейнерам, потерянным данным или состоянию, когда демон Docker больше не стартует.
Ниже — практичный план диагностики и уборки: как понять, что именно занимает диск, как использовать docker system df, когда безопасен prune, чем отличаются images, volumes и container logs, а также как настроить профилактику, чтобы проблема не возвращалась через неделю.
Что лежит в /var/lib/docker
Каталог /var/lib/docker — это рабочее хранилище Docker Engine. Конкретная структура зависит от версии Docker, storage driver и настроек, но на типичном Linux VDS вы увидите примерно такие подкаталоги:
overlay2— слои образов и файловые изменения контейнеров при storage driveroverlay2;containers— метаданные контейнеров и часто их JSON-логи;volumes— Docker volumes, где обычно лежат постоянные данные приложений;image— метаданные образов;buildkit— кэш сборок BuildKit;network— состояние сетей Docker;tmp— временные файлы.
Если диск забит Docker, почти всегда виноват один из четырёх источников: старые images, неиспользуемые volumes, разросшиеся container logs или кэш сборок.
Важно отличать «занимает место» от «можно удалить». Старый образ, который больше не используется контейнером, обычно можно удалить. А volume с данными PostgreSQL или uploads сайта — это уже рабочие данные, и удаление такого volume равносильно удалению базы или пользовательских файлов.
Быстрая диагностика: сколько занял Docker
Начинайте не с du, а с общей картины по файловой системе. Так вы поймёте, действительно ли проблема в Docker, или место заняли журналы системы, бэкапы, дампы баз, временные файлы веб-приложения.
df -h
sudo du -xhd1 /var | sort -h
sudo du -xhd1 /var/lib | sort -h
sudo du -xhd1 /var/lib/docker | sort -h
Ключ -x в du полезен на серверах, где есть отдельные mount point: он не даёт команде уходить на другие файловые системы. Если /var/lib/docker явно самый большой каталог, переходите к инструментам Docker.
Базовая команда для оценки хранилища Docker:
docker system df
Более подробный вывод:
docker system df -v
В результате вы увидите категории Images, Containers, Local Volumes и Build Cache. Колонка SIZE показывает общий размер, а RECLAIMABLE — сколько Docker считает потенциально освобождаемым. Это не всегда означает «удаляйте всё немедленно», но это хороший ориентир.

Как читать docker system df без самообмана
Вывод docker system df иногда удивляет: du показывает десятки гигабайт в /var/lib/docker, а Docker говорит, что reclaimable намного меньше. Это нормально. Файловая система overlay использует слои: один и тот же слой может быть общим для нескольких images и containers, а часть данных относится к активным контейнерам.
Разберём категории:
Images— скачанные и собранные образы. Один образ может использоваться несколькими контейнерами. Удалять можно те, которые не нужны и не используются.Containers— остановленные и запущенные контейнеры, включая writable layer. Если приложение пишет данные внутрь контейнера, а не в volume, размер может быстро расти.Local Volumes— постоянные тома. Здесь часто лежат базы данных, пользовательские загрузки, файлы CMS, очереди, индексы поиска.Build Cache— кэш Docker BuildKit. Он ускоряет сборки, но на CI-серверах и dev-стендах может занимать очень много.
Команда docker system df -v помогает увидеть, какие images и volumes выглядят крупными. Но перед удалением volume всегда выясняйте, кто его использует и что внутри.
overlay2: почему он большой и почему его нельзя чистить rm
Каталог overlay2 часто становится самым крупным внутри /var/lib/docker. Это не «мусорная папка», а основа работы контейнерной файловой системы. Внутри лежат read-only слои образов и writable слои контейнеров. Docker связывает их через свои метаданные, поэтому ручное удаление отдельных директорий внутри overlay2 почти всегда плохая идея.
Проверить используемый storage driver можно так:
docker info --format '{{.Driver}}'
Если вывод — overlay2, это стандартный и обычно правильный вариант для современных Linux-серверов. Большой размер overlay2 означает одно из следующих:
- на сервере много старых образов;
- контейнеры пишут временные или постоянные данные внутрь своего слоя;
- есть остановленные контейнеры с большим writable layer;
- накопился build cache;
- приложение пишет логи не только в stdout, но и в файлы внутри контейнера.
Чтобы найти контейнеры с большим writable layer, используйте:
docker ps -a --size
Если у контейнера размер writable layer измеряется гигабайтами, это сигнал: данные пишутся не туда. В production лучше выносить постоянные данные в volumes или bind mounts, а временные директории ограничивать и чистить средствами приложения.
Безопасная очистка images
Images занимают место после деплоев, экспериментов, локальных сборок и обновлений. На сервере может оставаться много старых тегов, dangling-слоёв и образов, которые уже не используются ни одним контейнером.
Посмотреть список образов:
docker images
Посмотреть dangling images — слои без тега:
docker images -f dangling=true
Удалить только dangling images обычно безопаснее всего:
docker image prune
Удалить все images, которые не используются контейнерами:
docker image prune -a
Вот здесь нужна осторожность. Команда docker image prune -a не удалит образ, который прямо используется существующим контейнером, но может удалить старые версии, нужные для быстрого rollback. Если у вас деплой построен так, что откат выполняется запуском старого образа с локального диска, после такой очистки откат потребует повторного скачивания или сборки.
Для более контролируемой уборки можно удалять конкретные образы:
docker rmi image_name:tag
docker rmi image_id
Хорошая практика для production: хранить образы в registry, тегировать релизы понятными immutable-тегами и не рассчитывать, что старый образ всегда останется на VDS. Тогда локальная очистка становится менее рискованной.
Volumes: где чаще всего лежат настоящие данные
Docker volumes — самая опасная зона для бездумного prune. В volume может лежать база PostgreSQL, каталог wp-content/uploads, файлы пользователей, состояние брокера очередей, индексы Elasticsearch или MinIO data directory. Если удалить такой volume, Docker не спросит у приложения, был ли это production.
Список volumes:
docker volume ls
Подробности по конкретному volume:
docker volume inspect volume_name
Понять, какие контейнеры используют volume, можно через inspect контейнеров:
docker ps -a --format '{{.Names}}'
docker inspect container_name --format '{{json .Mounts}}'
Если установлен jq, вывод удобнее читать так:
docker inspect container_name | jq '.[0].Mounts'
Неиспользуемые volumes Docker показывает как dangling:
docker volume ls -f dangling=true
Удалить все volumes, которые не используются ни одним контейнером:
docker volume prune
Команда выглядит безобидно, но в реальной эксплуатации бывают нюансы. Например, вы временно остановили контейнер базы, удалили сам контейнер при пересоздании compose-проекта, а volume остался dangling. Для Docker это «неиспользуемый volume», а для вас — единственная копия данных. Поэтому перед docker volume prune обязательно проверяйте имена volumes, compose-проекты и наличие актуального бэкапа.
Если нужно оценить размер volumes вручную:
sudo du -xhd1 /var/lib/docker/volumes | sort -h
А содержимое конкретного volume можно посмотреть через его mountpoint:
docker volume inspect volume_name --format '{{.Mountpoint}}'
sudo du -xhd1 /var/lib/docker/volumes/volume_name/_data | sort -h
Для named volumes путь обычно находится внутри /var/lib/docker/volumes. Для bind mounts данные могут лежать где угодно: например, в /srv/app/data, /opt/project или домашнем каталоге пользователя.
Container logs: тихий пожиратель диска
Очень частая причина переполнения /var/lib/docker — container logs. По умолчанию Docker часто использует logging driver json-file. Логи контейнера пишутся в JSON-файл внутри /var/lib/docker/containers. Если приложение активно пишет stdout/stderr, файл может вырасти до десятков гигабайт.
Проверить текущий logging driver:
docker info --format '{{.LoggingDriver}}'
Найти самые большие файлы логов контейнеров:
sudo find /var/lib/docker/containers -name '*-json.log' -printf '%s %p\n' | sort -n | tail -20
Если нужно сопоставить контейнер с файлом, посмотрите ID контейнера:
docker ps -a --no-trunc
Просмотр логов через Docker:
docker logs --tail 200 container_name
docker logs --since 1h container_name
Если лог уже огромный и нужно срочно освободить место, корректнее не удалять файл, а обнулить его. Сначала определите путь:
docker inspect container_name --format '{{.LogPath}}'
Затем обнулите файл:
sudo truncate -s 0 /var/lib/docker/containers/container_id/container_id-json.log
Замените путь на тот, который показала команда docker inspect. Не удаляйте лог-файл через rm: процесс может продолжить писать в удалённый inode, и место не освободится до перезапуска контейнера или Docker.
Чтобы проблема не повторялась, настройте ротацию логов Docker. Для демона Docker создайте или отредактируйте /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
}
}
После изменения конфигурации перезапустите Docker:
sudo systemctl restart docker
Важный нюанс: настройки логирования применяются к новым контейнерам. Уже созданные контейнеры обычно нужно пересоздать, чтобы они получили новые параметры.
В Docker Compose можно задать ротацию на уровне сервиса:
services:
app:
image: example/app:latest
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
После изменения compose-файла контейнер нужно пересоздать:
docker compose up -d --force-recreate
Build cache: полезен, пока не съел весь диск
На серверах, где выполняются сборки Docker images, быстро растёт Build Cache. Это особенно заметно в CI/CD: каждый pipeline скачивает зависимости, собирает слои, сохраняет промежуточные результаты. Кэш ускоряет сборку, но без лимитов превращается в отдельное хранилище мусора.
Посмотреть использование BuildKit-кэша:
docker builder du
Очистить неиспользуемый build cache:
docker builder prune
Более агрессивная очистка:
docker builder prune -a
Можно ограничить очистку по возрасту:
docker builder prune --filter until=168h
Такой вариант удалит кэш старше 168 часов, то есть примерно недели. Для production-сервера, где сборки редки, это обычно разумнее, чем полная очистка после каждого деплоя. Для CI-сервера политику стоит подбирать по балансу между скоростью сборки и стоимостью диска.
docker system prune: когда можно, а когда опасно
docker system prune — удобная команда комплексной уборки. Но именно из-за удобства она часто становится причиной неприятных сюрпризов. Сначала посмотрите, что вообще есть на сервере:
docker ps -a
docker images
docker volume ls
docker network ls
docker system df -v
Базовая очистка:
docker system prune
Она удаляет остановленные контейнеры, неиспользуемые networks, dangling images и часть build cache. Volumes по умолчанию не удаляются, и это хорошо.
Агрессивная очистка:
docker system prune -a
Она дополнительно удаляет все images, которые не используются контейнерами. Это освобождает больше места, но может убрать образы для rollback.
Самый опасный вариант:
docker system prune -a --volumes
Эта команда дополнительно удаляет неиспользуемые volumes. На тестовом стенде это может быть нормально. На production без проверки и бэкапа — плохая идея.
Если вы не уверены, что volume не содержит ценных данных, не включайте
--volumesв автоматическую очистку.
Более безопасная стратегия — чистить категории отдельно: сначала logs и dangling images, затем build cache по возрасту, и только после ручной проверки — volumes.
Пошаговый runbook: Docker занял весь диск
Когда место уже закончилось, действовать нужно спокойно. Цель — быстро освободить немного пространства, восстановить работу сервисов и только потом разбираться глубже.
Шаг 1. Проверить диск и inode
df -h
df -ih
Иногда проблема не в гигабайтах, а в inode: слишком много мелких файлов. Для Docker это встречается реже, чем для кэшей приложений, но проверка занимает секунды.
Шаг 2. Найти крупные зоны внутри Docker
sudo du -xhd1 /var/lib/docker | sort -h
docker system df -v
Если самым крупным оказался containers, почти наверняка выросли container logs. Если overlay2 — смотрите images, writable layers и build cache. Если volumes — не удаляйте ничего, пока не поймёте назначение томов.
Шаг 3. Срочно обнулить разросшийся лог
docker inspect container_name --format '{{.LogPath}}'
sudo truncate -s 0 /path/from/logpath
Это часто освобождает место сразу и без остановки контейнера. После этого настройте ротацию логов, иначе через несколько часов ситуация повторится.
Шаг 4. Удалить очевидный мусор
docker image prune
docker builder prune --filter until=168h
docker container prune
docker container prune удаляет остановленные контейнеры. Перед запуском проверьте, нет ли среди них контейнера, который вы специально держите остановленным для анализа или rollback.
Шаг 5. Проверить volumes вручную
docker volume ls -f dangling=true
sudo du -xhd1 /var/lib/docker/volumes | sort -h
Если найден огромный dangling volume, выясните его происхождение. Часто имя содержит префикс compose-проекта. Проверьте старые compose-файлы, документацию проекта и бэкапы. Удаляйте volume только после подтверждения, что данные не нужны.

Почему контейнеры начинают писать гигабайты внутрь себя
У Docker есть важная архитектурная особенность: всё, что приложение пишет не в volume и не в bind mount, оказывается в writable layer контейнера. Этот слой живёт в overlay2 и удаляется вместе с контейнером. Для временных файлов это допустимо, но для постоянных данных — опасно.
Типичные причины роста writable layer:
- приложение пишет файловые логи в
/var/logвнутри контейнера; - загрузки пользователей сохраняются в каталог приложения, но этот каталог не подключён как volume;
- кэш фреймворка, npm, pip, composer или thumbnails генерируется внутри контейнера;
- база данных запущена без volume для data directory;
- в контейнере выполняются периодические задачи, создающие дампы и архивы.
Проверить размер контейнеров можно командой:
docker ps -a --size
Если вы видите большой размер у работающего контейнера приложения, разберите его файловую систему:
docker exec -it container_name sh
du -xhd1 / | sort -h
В минимальных образах может не быть du или shell. Тогда лучше временно использовать debug-контейнер с подключением нужного volume или пересобрать образ с диагностическими инструментами для staging. В production не стоит хаотично устанавливать пакеты внутрь контейнера: изменения потеряются при пересоздании, а образ станет непредсказуемым.
Настройка лимитов логов на уровне Docker daemon
Ротация логов — обязательная настройка для одиночного Docker-хоста на VDS. Без неё даже корректное приложение может забить диск при ошибке: например, если backend попал в цикл исключений и пишет stack trace несколько раз в секунду.
Проверьте, есть ли файл /etc/docker/daemon.json:
sudo test -f /etc/docker/daemon.json && sudo cat /etc/docker/daemon.json
Минимальная конфигурация с ограничением размера логов:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "10"
}
}
Так каждый контейнер сможет хранить до десяти файлов по 20 МБ, то есть примерно 200 МБ логов. Для небольшого Linux VDS это уже лучше, чем бесконечный рост. Для шумных сервисов выбирайте лимиты осознанно: слишком маленькое окно логов усложнит расследование инцидентов.
После изменения проверьте JSON и перезапустите Docker:
sudo dockerd --validate --config-file /etc/docker/daemon.json
sudo systemctl restart docker
Если команда валидации недоступна в вашей версии, используйте проверку через python3:
python3 -m json.tool /etc/docker/daemon.json
После перезапуска пересоздайте контейнеры, для которых должны примениться новые настройки. Для compose-проекта:
docker compose up -d --force-recreate
Если контейнеры обслуживают публичный сайт за Nginx, следите не только за Docker-логами, но и за кэшем веб-сервера. Например, при раздаче изображений полезно заранее продумать правила хранения и инвалидации — похожий подход разобран в материале про WebP и AVIF в Nginx с map и cache.
Особенности путей и systemd в разных дистрибутивах
Сами команды Docker в Debian, Ubuntu, AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux и Fedora обычно одинаковые. Отличия чаще касаются установки пакетов, расположения drop-in файлов systemd и наличия SELinux в RHEL-based системах.
Debian и Ubuntu
Проверить сервис Docker и логи systemd:
systemctl status docker
journalctl -u docker -n 200 --no-pager
Основной конфигурационный файл Docker daemon тот же:
/etc/docker/daemon.json
AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux и Fedora
Команды Docker такие же, но на RHEL-based системах чаще включён SELinux. Если вы переносите Docker data-root или используете bind mounts, неправильный SELinux-контекст может привести к ошибкам доступа. Для диагностики смотрите audit-журнал:
sudo ausearch -m avc -ts recent
sudo journalctl -t setroubleshoot --no-pager
Не отключайте SELinux только потому, что контейнер не видит каталог. Обычно лучше назначить корректный контекст или использовать поддерживаемые параметры монтирования. После переноса данных Docker проверяйте не только права Unix, но и политики безопасности.
Что делать, если /var/lib/docker не помещается на системном диске
Иногда очистка помогает ненадолго: проект растёт, images и volumes объективно занимают много места. В этом случае лучше не бороться с симптомами, а перенести Docker data-root на отдельный диск или увеличить размер VDS-диска. Отдельный раздел под Docker удобен тем, что переполнение контейнерного хранилища не сразу ломает всю систему, SSH, пакетный менеджер и системные журналы.
Текущий Docker root dir:
docker info --format '{{.DockerRootDir}}'
Если вы планируете перенос, общий безопасный порядок такой:
- сделать бэкап важных volumes и compose-файлов;
- остановить контейнеры;
- остановить Docker;
- скопировать данные с сохранением прав, владельцев и xattrs;
- указать новый
data-rootв/etc/docker/daemon.json; - запустить Docker и проверить контейнеры;
- удалять старый каталог только после проверки.
Пример настройки data-root:
{
"data-root": "/srv/docker",
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
}
}
Копирование можно выполнить через rsync после остановки Docker:
sudo systemctl stop docker
sudo rsync -aHAX /var/lib/docker/ /srv/docker/
sudo systemctl start docker
Перед удалением старых данных проверьте:
docker info --format '{{.DockerRootDir}}'
docker ps
docker system df
Не спешите удалять /var/lib/docker сразу. Лучше оставить его на короткое время под другим именем, если позволяет место, или иметь полный бэкап. При переносе на RHEL-based системах дополнительно учитывайте SELinux-контексты.
Автоматическая очистка: что можно поставить в cron или systemd timer
Автоматизация полезна, но только для безопасных операций. Не стоит каждую ночь запускать docker system prune -a --volumes на production. Это похоже на уборку склада бульдозером: свободное место появится, но вместе с ним могут исчезнуть важные данные.
Относительно безопасный набор для регулярной уборки:
docker image prune -f
docker builder prune -f --filter until=168h
docker container prune -f --filter until=168h
Volumes лучше чистить вручную или по строгому регламенту, где есть бэкап и понятные имена. Например, можно договориться, что временные volumes имеют префикс tmp_, а production volumes — префикс проекта и окружения. Тогда вероятность ошибки ниже.
Для контроля места добавьте простой мониторинг. Минимум — alert по занятости файловой системы и inode. Лучше — отдельные метрики по Docker root dir, логам контейнеров и volumes. Даже простая проверка раз в час даст время отреагировать до того, как база данных начнёт падать с ошибкой No space left on device.
Чек-лист безопасной уборки Docker на Linux VDS
- Проверьте
df -hиdf -ih, чтобы понять, закончились блоки или inode. - Сравните
duпо/var/lib/dockerс выводомdocker system df -v. - Не удаляйте вручную каталоги внутри
overlay2,containersиvolumes. - Для логов используйте
truncate, а затем настройтеmax-sizeиmax-file. - Сначала чистите dangling images и старый build cache.
- Перед
docker image prune -aубедитесь, что rollback не зависит от локальных старых images. - Перед
docker volume pruneпроверьте назначение каждого volume и наличие бэкапа. - Следите за контейнерами с большим writable layer через
docker ps -a --size. - Для растущих проектов рассмотрите отдельный диск или перенос
data-root. - Настройте мониторинг свободного места, чтобы не узнавать о проблеме от пользователей.
Итоги
/var/lib/docker растёт не сам по себе: место занимают images, volumes, container logs, writable layers в overlay2 и build cache. Правильный подход — сначала измерить, затем понять назначение данных и только после этого удалять. Команды docker system df, docker ps -a --size, docker volume inspect и проверка логов дают почти всю нужную картину.
Для быстрой уборки обычно достаточно обнулить разросшийся JSON-log, выполнить docker image prune и почистить старый build cache. Для долгосрочного порядка настройте ротацию логов, храните данные в понятных volumes, не пишите постоянные файлы внутрь контейнера и заведите мониторинг диска. Тогда Docker на Linux VDS останется удобным инструментом, а не внезапным источником ночных аварий.


