Docker Compose: шпаргалка по командам и грабли

Как Compose находит проект, запуск и остановка, логи, exec и run, обновление образов, профили, несколько файлов и какие флаги удаляют данные.

2 ноября 2026 г.·3 мин чтения·docker, compose

Команды Compose v2 (docker compose, через пробел), сгруппированные по задачам. Рядом с каждой группой — типичные ошибки, которые стоят часов отладки.

Все команды проверены на VPS с Ubuntu 24.04 и Debian 12 — на tanaka.host оба образа ставятся при заказе сервера, а если что-то пойдёт не так, выручит консоль в кабинете.

Как Compose находит проект

  • Файл. Без -f ищется compose.yaml (также compose.yml, docker-compose.yaml, docker-compose.yml) в текущей папке, потом в родительских. compose.override.yaml подмешивается автоматически.
  • Имя проекта. Берётся из -p, переменной COMPOSE_PROJECT_NAME, поля name: в файле или имени папки — в таком порядке. Оно становится префиксом контейнеров, сетей и томов: /opt/blog/compose.yaml → blog-web-1, blog_default, blog_db_data.
  • Переменные подставляются из окружения и файла .env рядом с compose-файлом.

Правило: сначала cd в папку проекта, потом команда. «Контейнеры пропали» в 90% случаев означает, что вы не в той папке.

Запуск и остановка

docker compose up -d              # поднять всё в фоне
docker compose up -d app          # только один сервис (и его зависимости)
docker compose up -d --wait       # дождаться healthy
docker compose stop               # остановить, контейнеры остаются
docker compose start              # запустить остановленные
docker compose restart app        # перезапустить сервис
docker compose down               # остановить и удалить контейнеры и сети
docker compose kill               # убить немедленно

Грабли:

  • restart не применяет изменения в compose.yaml и .env. Для этого нужен up -d — он пересоздаст только то, что поменялось.
  • up без -d привязывается к терминалу, и Ctrl+C останавливает всё.
  • stop сохраняет контейнеры, down удаляет их. Именованные тома переживают и то, и другое.
  • На остановку даётся 10 секунд, потом SIGKILL. Для баз данных поставьте stop_grace_period: 60s в сервисе или docker compose stop -t 60.

Состояние и логи

docker compose ps                 # сервисы проекта
docker compose ps -a              # включая остановленные
docker compose ls                 # все проекты на сервере
docker compose logs -f            # логи всех сервисов
docker compose logs -f --tail 100 app
docker compose logs --since 30m
docker compose top                # процессы внутри контейнеров
docker compose stats              # CPU и память

logs без --tail выведет всё с момента создания контейнера. ls показывает путь к compose-файлу каждого проекта — помогает найти «потерянный».

Команды внутри контейнеров

docker compose exec app sh                          # шелл в работающем контейнере
docker compose exec -u root app sh                  # под root
docker compose exec db psql -U postgres
docker compose exec -T db pg_dump -U postgres mydb > dump.sql   # -T для вывода в файл и cron
docker compose run --rm app sh                      # новый временный контейнер
docker compose run --rm --no-deps app ./migrate
docker compose cp app:/app/config.yml ./config.yml
docker compose cp ./config.yml app:/app/config.yml

exec заходит в уже запущенный контейнер, run создаёт новый — всегда с --rm, иначе они копятся. run не публикует порты (нужен --service-ports). В контейнере может не быть bash — пробуйте sh.

Образы и обновление

docker compose pull               # скачать свежие образы
docker compose build              # собрать из Dockerfile
docker compose build --no-cache
docker compose up -d --build      # собрать и поднять
docker compose up -d --force-recreate
docker compose up -d --pull always

Стандартное обновление:

docker compose pull
docker compose up -d
docker image prune -f

pull сам по себе ничего не меняет — контейнеры продолжают работать на старом образе до up -d. --force-recreate не скачивает и не собирает образ, только пересоздаёт контейнеры.

Проверка конфигурации

docker compose config             # итоговый конфиг с подставленными переменными
docker compose config --quiet     # только проверить синтаксис
docker compose config --services
docker compose --dry-run up -d    # показать, что будет сделано

config печатает секреты из .env — не вставляйте вывод в чаты. Привычка config --quiet && docker compose up -d экономит много нервов.

Несколько файлов и проектов

docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose -f /opt/blog/compose.yaml ps
docker compose -p staging up -d
docker compose --env-file .env.prod up -d

Те же -f и -p нужно повторять в каждой команде, иначе Compose не найдёт проект. Удобнее задать их один раз в .env:

COMPOSE_FILE=compose.yaml:compose.prod.yaml
COMPOSE_PROJECT_NAME=blog

Профили

Сервисы, которые нужны не всегда (админка, отладочные инструменты):

services:
  db:
    image: postgres:18
  adminer:
    image: adminer
    profiles: [tools]
    ports:
      - "127.0.0.1:8081:8080"
docker compose up -d                      # только db
docker compose --profile tools up -d      # db и adminer
docker compose --profile tools down       # down без профиля adminer не увидит

Очистка и опасные флаги

Команда Удаляет данные?
docker compose down Нет
docker compose down --remove-orphans Нет, убирает контейнеры удалённых из файла сервисов
docker compose down --rmi all Нет, образы скачаются заново
docker compose down -v Да, удаляет именованные тома вместе с базой
docker compose up -d -V Анонимные тома
docker volume prune --all Да, все тома, которые сейчас не используются, включая проекты в состоянии down

Тома с external: true Compose не удаляет никогда. Перед down -v сделайте дамп.

Переход с docker-compose v1

v1 (Python, команда через дефис) не поддерживается с 2023 года. Файлы docker-compose.yml продолжают работать, но:

  • имена контейнеров меняются с project_web_1 на project-web-1 — проверьте скрипты и мониторинг;
  • поле version: игнорируется, его можно удалить;
  • docker compose up -d в папке проекта пересоздаст контейнеры с новыми именами, тома останутся.

Для старых скриптов, вызывающих docker-compose, достаточно обёртки:

printf '#!/bin/sh\nexec docker compose "$@"\n' | sudo tee /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose