Прямой ответ
Локальный dev-стек (API + БД + кэш + очереди) на Docker Compose собирается надёжно, если соблюдать четыре правила: тяжёлые сервисы стартуют по профилям, а не всегда; у каждого сервиса заданы лимиты памяти; данные живут в именованных volumes, а не в bind mounts из Windows; порядок запуска задан healthcheck'ами, а не depends_on: condition: service_started. Эти четыре строки конфигурации устраняют 90% «почему ноутбук лёг» и «почему БД не поднялась» в локальной разработке.
Структура compose-файла, которая масштабируется
Базовый стек «API + Postgres + Redis» разрастается до десятка сервисов; держите его управляемым:
services:
api:
build: .
ports: ["8000:8000"]
depends_on:
db: { condition: service_healthy }
mem_limit: 1g
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes: [pgdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 10
mem_limit: 1g
redis:
image: redis:7-alpine
mem_limit: 256m
volumes:
pgdata:
Ключевые моменты: pg_isready в healthcheck — приложение стартует, когда БД реально принимает подключения, а не когда контейнер «создан»; mem_limit не даёт одному сервису выдавить остальные; секреты приходят из .env, а не хардкодом — правила обращения с ними в статье про секреты и .env.
Профили: тяжёлое — по запросу
Elasticsearch, Kafka, мониторинг нужны не каждый день. Профили Compose решают это штатно:
elasticsearch:
image: elasticsearch:8.14.0
profiles: ["full"]
mem_limit: 2g
docker compose up поднимает базовый стек; docker compose --profile full up — расширенный. Типичная экономия на 16 ГБ ноутбуке — 3–6 ГБ памяти, которые Elasticsearch/Kafka/Prometheus съедали бы постоянно. Альтернатива профилям — отдельные compose-файлы (-f base.yml -f observability.yml), удобная для CI.
Volumes против bind mounts: вопрос скорости
Два сценария, два разных механизма:
- Данные сервисов (БД, кэши) — только именованные volumes (
pgdata:). Внутри WSL2/VM они живут в ext4 и работают на полной скорости NVMe. - Ваш код — bind mount допустим для hot-reload, но на Windows держите проект внутри WSL (
~/projects): bind mount из NTFS (/mnt/c/...) проходит через трансляцию файловых вызовов и замедляет I/O в разы — это же правило объяснено в статье про WSL2 и Docker.
Симптом ошибки — миграции и тесты, которые «на сервере летают, на ноутбуке ползают»: почти всегда это bind-mounted БД-каталог или код на NTFS.
Лимиты и приоритеты на ноутбуке
- Считайте бюджет: сумма
mem_limitактивных сервисов + 1–2 ГБ на Docker-движок должна влезать в лимит WSL2/Docker Desktop. - Для Elastic/JVM-сервисов задавайте и heap (
ES_JAVA_OPTS=-Xms512m -Xmx512m) — иначе JVM заберёт память под себя сверх лимита контейнера. docker compose up -d --scale worker=3для локальной проверки конкурентности; не забывайте, что каждая реплика — своя память.- Мониторинг:
docker statsв отдельном терминале; контейнер сOOMKilled: trueвdocker inspect— признак слишком жёсткогоmem_limit.
Порядок отладки стека
docker compose ps— что живо, что restarting.docker compose logs -f <svc>— ошибка сервиса; у БД смотрите первые 50 строк (инициализация).- Порт занят на хосте?
docker compose configпокажет итоговые порты; конфликт лечится сменой внешнего порта ("8080:8000"). - «Работало вчера, сегодня не поднимается» — чаще всего испорченный volume после аварийного останова:
docker compose down(без-v), затем проверка логов инициализации;-v(удаление данных) — только осознанно. - Полный сброс без потери образа:
docker compose down --rmi local --remove-orphans.
Когда сервисов становится больше десяти и появляются сервис-дискавери/ингрессы — это сигнал посмотреть в сторону локального Kubernetes: сравнение k3s/minikube/kind и их бюджеты памяти — в отдельной статье про Kubernetes на ноутбуке.
Сценарий дня: экономим ресурсы без отказа от стека
Реалистичный рабочий цикл на 16 ГБ ноутбуке выглядит так, и это экономит 2–4 ГБ в течение дня:
- Утром:
docker compose up -d db redis— только то, что нужно сейчас; API-сервис запускаете из IDE, а не контейнером (hot-reload быстрее, память дешевле). - Понадобились очереди/поиск:
docker compose --profile full up -d— тяжёлые сервисы стартуют на время задачи. - Обед/встреча:
docker compose stop— данные в volumes сохраняются, память освобождается мгновенно; возобновление —startза секунды. - Вечером:
docker compose down(без-v!) плюс раз в неделюdocker builder prune -f— кэш сборок растёт незаметно. - Раз в месяц: проверка
docker system dfи удаление висячих образов (docker image prune -a— осторожно, удалит неиспользуемые теги; при следующем запуске они скачаются заново).
Эта дисциплина важнее любой разовой настройки: «вечно поднятый» стек из десяти сервисов — главная причина, по которой разработчики на 16 ГБ живут в swap и не понимают почему. Для контроля суммарного бюджета (Docker + IDE + браузер) используйте методику измерения из статьи про WSL2 и Docker — там же описаны лимиты на стороне гипервизора.
FAQ
Нужен ли Docker Desktop для Compose? Нет: Compose v2 — плагин Docker Engine, на Linux/в WSL2 работает без Desktop. Desktop даёт GUI и интеграцию с Windows/macOS — вопрос удобства, не функциональности.
Как обновить образы стека без простоя данных?
docker compose pull && docker compose up -d: пересоздадутся только изменившиеся сервисы, volumes не тронутся. Мажорные версии БД обновляйте по документации вендора (dump/restore или штатный апгрейд).
Почему depends_on не гарантирует готовность БД?
По умолчанию он ждёт только старта контейнера. Готовность определяет healthcheck + condition: service_healthy — без него приложение стартует раньше БД и падает.
Можно ли держать стек запущенным постоянно?
Можно, но считайте память: «дежурные» Postgres+Redis — это ~0,7–1,5 ГБ постоянно. На 16 ГБ ноутбуке разумнее поднимать стек на время работы: docker compose stop сохраняет данные и освобождает память.
Чем заменить тяжёлые сервисы в локальном стеке?
Для поиска вместо Elasticsearch часто хватает Postgres FTS или Meilisearch (в 10 раз легче по памяти); вместо Kafka в dev-сценариях — Redis Streams; вместо полноценного мониторинга — точечные логи и docker stats. Принцип: локальный стек должен проверять логику приложения, а не воспроизводить прод один в один.
Источники: документация Docker Compose (профили, healthchecks), Docker Engine storage (ссылки в шапке).