Docker: как удалить контейнер — все способы и команды

Контейнеры размножаются быстрее, чем вы успеваете их останавливать: одна отладка — пять новых записей в docker ps -a, каждая со своим слоем, сетью и томом. Рано или поздно место на диске заканчивается, а вывод команды превращается в свалку из давно забытых сервисов. Удаление контейнера — операция на одну команду, но именно здесь всплывают нюансы: почему docker rm отказывается работать, что делать с запущенным контейнером и как убрать всё разом, не снеся лишнего.
Docker не даст удалить работающий контейнер — попытка завершится ошибкой Error response from daemon: You cannot remove a running container. Это не баг, а защита от случайного убийства сервиса. Логика простая: сначала контейнер останавливают, потом убирают. При удалении исчезает записываемый слой (writable layer) и метаданные. Образы, именованные тома и пользовательские сети при этом остаются на месте — если явно не попросить обратное. Вот почему после docker rm место на диске освобождается не всегда: основной объём часто занимают не контейнеры, а образы и тома.
Перед любой уборкой стоит оценить масштаб бедствия. docker ps -a покажет все контейнеры, включая остановленные, а docker system df — сколько места занимает каждый тип объектов. Дальше работа идёт по ситуации:
- Удалить остановленный контейнер по имени или ID: docker rm web-server или docker rm a1b2c3d4e5f6.
- Убрать несколько сразу: docker rm web db cache.
- Вместе с анонимными томами: docker rm -v worker-1.
- Принудительно, если контейнер запущен: docker rm -f my_container — это SIGKILL и удаление одной командой.
По умолчанию принимается и короткий ID, и полный, и имя. Достаточно первых трёх-четырёх символов хеша, если они однозначны. Но с флагом -f стоит быть осторожнее. Корректное удаление начинается с корректной остановки: docker stop отправляет процессу SIGTERM и даёт время завершиться, docker kill бьёт SIGKILL сразу. Для stateless-сервисов разницы почти нет, а вот для PostgreSQL, Redis с сохранением на диск или брокера сообщений принудительный вариант — плохая идея. Контейнер не успеет сбросить буферы, и при следующем запуске вы можете увидеть восстановление из WAL или, в худшем случае, повреждённые данные. Для баз и очередей окно ожидания лучше увеличить: docker stop -t 30 postgres.
Важно: docker rm -f не удаляет тома. Именованные тома останутся на диске — и это правильно, так данные переживут пересоздание контейнера. Анонимные тома без флага -v превратятся в мусор, который придётся чистить отдельно.
Когда контейнеров десятки, поштучный docker rm превращается в бессмысленную работу. Docker CLI даёт штатные инструменты для пакетной уборки. docker container prune удалит все остановленные контейнеры, с подтверждением или без него (-f), а фильтр --filter "until=24h" ограничит чистку возрастом. Радикальный вариант — docker rm -f $(docker ps -aq), но безопаснее через xargs -r: команда не упадёт, если контейнеров нет. Полная очистка системы выполняется через docker system prune -a, а с флагом --volumes сносит ещё и тома с данными.
Флаг -a у docker system prune расширяет чистку на все образы, у которых нет связанного контейнера, — включая те, что вы не собирали, а просто скачали из реестра. Следующая сборка заново вытянет базовые слои. Если трафик платный или интернет медленный, лучше этого избегать.
Удаление контейнера почти никогда не возвращает диск к исходному состоянию. Остаются три категории объектов: именованные тома, которые живут отдельно и удаляются только через docker volume rm или docker volume prune; анонимные тома, созданные при docker run -v /data без имени; и образы — контейнер лишь ссылается на образ, и пока есть хотя бы один дочерний контейнер, docker image rm откажется работать без -f. Посмотреть реальную картину поможет docker system df -v.
В проектах с композ-файлом ручное удаление контейнеров не нужно — достаточно одной команды, которая разберёт стек по описанию из docker-compose.yml. docker compose down остановит и удалит контейнеры и сети проекта, флаг -v добавит именованные тома, --rmi all — собранные образы, а -t 0 снимет контейнеры без graceful shutdown. Без флага -v именованные тома остаются нетронутыми: пересобрали стек — данные на месте. Но именно из-за этого docker compose down, запущенный десять раз подряд, почти не освобождает диск.
Практика показывает: 90% проблем с забитым диском — это не контейнеры, а забытые тома и образы без тегов. После активного спринта сервер может потерять 20 ГБ, хотя docker ps -a показывает десяток контейнеров. Ответ кроется в docker system df: сотня dangling-образов и добрый десяток анонимных томов от экспериментов. Рабочая связка для профилактики — docker container prune -f --filter "until=48h" и docker image prune -f дважды в неделю, без -a и без --volumes, чтобы точно ничего не снести.
Короткая шпаргалка по итогам: для одного контейнера — docker stop и docker rm; для запущенного тестового — docker rm -f; для десятков остановленных — docker container prune; для проекта целиком — docker compose down; для полной ревизии диска — docker system prune -a, но с ясной головой и списком томов под рукой. И помните: удалить контейнер — это ещё не убрать за ним.


