Docker Compose — инструмент для запуска многоконтейнерных приложений через один файл конфигурации. Вместо того чтобы поднимать каждый контейнер руками через docker run, вы описываете весь стек в docker compose файл формата YAML — и запускаете одной командой. Docker compose установка занимает пять минут, а экономит часы работы.
В этой статье — всё что нужно: установка, структура файла, основные команды, сети, volumes и разбор реального примера со стеком Nginx + Node.js + PostgreSQL.
Что такое Docker Compose
Обычный Docker запускает один контейнер одной командой. Реальные приложения состоят из нескольких частей: веб-сервер, бэкенд, база данных, кэш. Запускать их по одному, следить за зависимостями, настраивать сети вручную — боль.
Docker Compose решает это через концепцию services. Каждый сервис — это контейнер (или группа контейнеров). Все сервисы описываются в одном docker-compose.yml файле и управляются как единое целое.
Docker vs Docker Compose — в чём разница
| Docker | Docker Compose |
|---|---|
| Один контейнер за раз | Несколько контейнеров одновременно |
| Команды в терминале | Конфигурация в YAML-файле |
| Сети и volumes — вручную | Автоматически создаёт сети и volumes |
| Нет понятия зависимостей | depends_on — порядок запуска |
| Для одиночных задач | Для dev-окружения и простых прод-стеков |
Когда использовать Compose: локальная разработка, тестовые окружения, небольшие продакшен-стеки на одном сервере.
Когда переходить на Kubernetes: нужна высокая доступность, масштабирование на несколько серверов, сложный оркестратор.
Установка Docker Compose
Есть два варианта Docker Compose — важно не путать:
- docker-compose (со знаком дефис) — старая standalone-утилита, написана на Python, устарела
- docker compose (без дефиса) — современный плагин для Docker CLI, написан на Go, рекомендуется
Используйте плагин. Если где-то встречаете docker-compose в инструкциях — это значит статья старая.
Docker Compose установка на Ubuntu
Способ 1 — через официальный репозиторий Docker (рекомендуется)
Устанавливаем Docker Engine вместе с плагином Compose:
# Удалить старые версии sudo apt remove docker docker-engine docker.io containerd runc # Установить зависимости sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release # Добавить GPG-ключ Docker sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # Добавить репозиторий echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # Установить <a title="Установка и настройка SOCKS5 proxy Dante в Docker Compose" href="https://it-apteka.com/ustanovka-i-nastrojka-socks5-proxy-dante-v-docker-compose/" target="_blank" rel="noopener" data-wpil-monitor-id="521">Docker + плагин Compose</a> sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Способ 2 — через apt (быстро, но не всегда последняя версия)
sudo apt update sudo apt install -y docker.io docker-compose-plugin
Способ 3 — бинарник (legacy, для старых систем)
# Скачать последний релиз sudo curl -L \ "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" \ -o /usr/local/bin/docker-compose # Дать права на выполнение sudo chmod +x /usr/local/bin/docker-compose
⚠️ Этот способ даёт старый docker-compose (с дефисом). Используйте только если нет другого выхода.
Проверка установки:
# Версия плагина (современный способ) docker compose version # Версия standalone (если стоит старый) docker-compose --version
Вывод должен быть примерно таким:
Docker Compose version v2.24.0
Если видите v2.x — всё хорошо. Если v1.x — пора обновляться.
Docker Compose file — структура и синтаксис
Весь стек описывается в файле docker-compose.yml (или docker-compose.yaml — оба варианта работают). Это и есть docker compose yaml конфигурация.
Основные секции файла
services: # Контейнеры приложения volumes: # Постоянное хранилище данных networks: # Сетевая изоляция
Директива version в современных версиях Compose не нужна — она устарела начиная с Compose v2. Если встречаете version: "3.8" в старых инструкциях — это нормально, просто наследие.
Полный пример docker-compose.yml
Разворачиваем стек: Nginx → Node.js backend → PostgreSQL:
services:
nginx:
image: nginx:alpine
container_name: nginx_proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- static_files:/var/www/static
depends_on:
- backend
networks:
- frontend
restart: unless-stopped
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: node_backend
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=myapp
- DB_USER=appuser
- DB_PASS=secretpassword
volumes:
- ./backend:/app
- /app/node_modules
depends_on:
postgres:
condition: service_healthy
networks:
- frontend
- backend
restart: unless-stopped
postgres:
image: postgres:16-alpine
container_name: postgres_db
environment:
POSTGRES_DB: myapp
POSTGRES_USER: appuser
POSTGRES_PASSWORD: secretpassword
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d myapp"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
postgres_data:
static_files:
networks:
frontend:
driver: bridge
backend:
driver: bridge
Разбор секций
services — основная секция. Каждый ключ внутри — отдельный сервис (контейнер). Имя сервиса становится именем хоста внутри сети Compose.
image / build — либо готовый образ из Docker Hub, либо сборка из локального Dockerfile.
ports — проброс портов: "хост:контейнер". Без этого сервис доступен только внутри сети Compose.
environment — переменные окружения. Лучше выносить секреты в .env файл и использовать ${VARIABLE}.
depends_on — порядок запуска. С condition: service_healthy ждёт пока healthcheck не пройдёт.
restart — политика перезапуска: no, always, unless-stopped, on-failure.
volumes — монтирование данных (подробнее ниже).
networks — к каким сетям подключён сервис.
Основные команды Docker Compose
Все docker compose command выполняются из папки, где лежит docker-compose.yml.
docker compose up
Запустить весь стек:
# Запустить в foreground (с выводом логов) docker compose up # Запустить в фоне (detached mode) docker compose up -d # Пересобрать образы и запустить docker compose up -d --build # Запустить только один сервис docker compose up -d postgres
docker compose down
Остановить и удалить контейнеры:
# Остановить контейнеры docker compose down # Остановить и удалить volumes (осторожно — данные удалятся!) docker compose down -v # Остановить и удалить образы docker compose down --rmi all
docker compose ps
Статус контейнеров в стеке:
docker compose ps # Расширенный вывод docker compose ps -a
docker compose logs
# Логи всех сервисов docker compose logs # Логи конкретного сервиса docker compose logs backend # Следить за логами в реальном времени docker compose logs -f # Последние 100 строк docker compose logs --tail=100 postgres
docker compose build
# Собрать все образы docker compose build # Собрать без кэша docker compose build --no-cache # Собрать конкретный сервис docker compose build backend
Остальные полезные команды
# Перезапустить сервис docker compose restart backend # Выполнить команду внутри контейнера docker compose exec backend sh docker compose exec postgres psql -U appuser -d myapp # Запустить одноразовую команду в новом контейнере docker compose run --rm backend npm run migrate # Остановить без удаления docker compose stop # Посмотреть конфигурацию после подстановки переменных docker compose config # Версия docker compose version
Docker Compose контейнеры и сервисы
Каждый docker compose service — это шаблон для создания контейнера. По умолчанию из одного сервиса создаётся один docker compose container.
Имя контейнера формируется автоматически: имя_проекта-имя_сервиса-номер. Например: myapp-backend-1. Проект — это имя папки, где лежит docker-compose.yml. Можно задать явно:
docker compose -p myproject up -d
Масштабирование сервисов
# Запустить 3 экземпляра backend docker compose up -d --scale backend=3
Через файл (современный способ):
services:
backend:
image: myapp/backend
deploy:
replicas: 3
⚠️ При масштабировании нельзя использовать фиксированный container_name и проброс конкретного порта хоста — будет конфликт.
Docker Compose network
По умолчанию Compose создаёт одну сеть для всего стека — все сервисы в ней видят друг друга по имени сервиса. Это и есть DNS-resolution внутри Compose.
# Из контейнера backend обращаемся к postgres вот так: # host: postgres (имя сервиса, не IP) # port: 5432 (внутренний порт, не пробрасываемый)
Кастомные сети
Разделение на сети — best practice для изоляции. В нашем примере выше:
frontend— nginx и backend: фронт видит бэкbackend— backend и postgres: бэк видит базу- nginx не видит postgres напрямую — изоляция
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # Нет доступа в интернет из этой сети
Подключение к внешней сети
networks:
shared_network:
external: true # Сеть создана вне Compose, подключаемся к ней
# Создать сеть заранее docker network create shared_network
Docker Compose volumes
Без volumes данные живут только пока живёт контейнер. Удалили контейнер — данные исчезли. Docker compose volumes решают эту проблему.
Типы монтирования
Named volume — для данных БД и постоянного хранилища:
services:
postgres:
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data: # Объявляем именованный volume
Данные хранятся в /var/lib/docker/volumes/имя_проекта_postgres_data/. Docker управляет этим сам.
Bind mount — для разработки, когда нужно монтировать локальную папку:
services:
backend:
volumes:
- ./backend:/app # Локальная папка → внутрь контейнера
- /app/node_modules # Анонимный volume для node_modules
Bind mount позволяет видеть изменения кода в реальном времени без пересборки образа.
Read-only mount — для конфигов:
volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro
Как не потерять данные БД
Три правила:
- Всегда используйте named volume для данных PostgreSQL, MySQL, MongoDB
- Никогда не делайте
docker compose down -vв продакшене без бэкапа - Регулярно бэкапьте volume отдельно от бэкапа контейнера
# Бэкап PostgreSQL из Compose docker compose exec postgres pg_dump -U appuser myapp > <a class="wpil_keyword_link" href="https://it-apteka.com/category/rezervnoe-kopirovanie/" target="_blank" rel="noopener" title="Резервное копирование" data-wpil-keyword-link="linked" data-wpil-monitor-id="534">backup</a>.sql # Восстановление docker compose exec -T postgres psql -U appuser myapp < backup.sql
Практический пример — разворачиваем стек Nginx + Node.js + PostgreSQL
Поднимаем продакшен-стек с нуля. Структура проекта:
myapp/
├── docker-compose.yml
├── docker-compose.dev.yml
├── .env
├── nginx/
│ └── nginx.conf
└── backend/
└── Dockerfile
Шаг 1 — Создаём .env файл с секретами:
# .env POSTGRES_DB=myapp POSTGRES_USER=appuser POSTGRES_PASSWORD=supersecret NODE_ENV=production
Шаг 2 — Production docker-compose.yml:
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- backend
networks:
- frontend
restart: unless-stopped
backend:
build: ./backend
environment:
- NODE_ENV=${NODE_ENV}
- DB_HOST=postgres
- DB_NAME=${POSTGRES_DB}
- DB_USER=${POSTGRES_USER}
- DB_PASS=${POSTGRES_PASSWORD}
depends_on:
postgres:
condition: service_healthy
networks:
- frontend
- backend
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
retries: 5
restart: unless-stopped
volumes:
postgres_data:
networks:
frontend:
backend:
Шаг 3 — Dev-конфигурация docker-compose.dev.yml (override):
services:
backend:
build:
context: ./backend
target: development
environment:
- NODE_ENV=development
volumes:
- ./backend:/app
- /app/node_modules
command: npm run dev
postgres:
ports:
- "5432:5432" # В dev пробрасываем порт наружу для подключения из IDE
Шаг 4 — Запуск:
# Продакшен docker compose up -d # Разработка (объединяет оба файла) docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
Шаг 5 — Проверка:
# Статус контейнеров docker compose ps # Логи docker compose logs -f # Проверить что база поднялась docker compose exec postgres pg_isready -U appuser
Шаг 6 — Перезапуск после обновления кода:
# Пересобрать и перезапустить только backend docker compose up -d --build backend
Шаг 7 — Полная остановка:
# Остановить без потери данных docker compose down # Остановить и удалить всё включая volumes (осторожно!) docker compose down -v
Частые ошибки Docker Compose
Порт уже занят
Error: Bind for 0.0.0.0:80 failed: port is already allocated
# Найти что занимает порт sudo ss -tlnp | grep :80 # Или sudo lsof -i :80 # Остановить процесс или изменить порт в docker-compose.yml
network not found
Error: network myapp_frontend declared as external, but could not be found
# Создать сеть вручную docker network create myapp_frontend # Или убрать external: true из конфига
Неправильный YAML — самая частая причина «ничего не работает»
# Проверить синтаксис файла docker compose config # Типичные ошибки: # - Табы вместо пробелов # - Неправильные отступы # - Кавычки не закрыты
permission denied при bind mount
# Проблема: контейнер запускается под другим пользователем
# Решение 1: задать uid/gid
services:
backend:
user: "1000:1000"
# Решение 2: изменить права на хосте
sudo chown -R 1000:1000 ./backend
Контейнер перезапускается в цикле
# Посмотреть почему падает docker compose logs backend # Проверить exit code docker compose ps -a
depends_on не помогает — сервис стартовал, но не готов
depends_on по умолчанию ждёт только запуска контейнера, не его готовности. Используйте healthcheck + condition: service_healthy — пример в файле выше.
Конфликт версий образа
# Всегда указывайте тег образа явно image: postgres:16-alpine # Хорошо image: postgres # Плохо — может обновиться и сломать всё # Обновить образы принудительно docker compose pull docker compose up -d
Чек-лист Docker Compose
- ☐ Установлен Docker Compose v2 (
docker compose version) - ☐ Секреты вынесены в
.envфайл, не захардкожены в yml - ☐
.envдобавлен в.gitignore - ☐ Для БД используется named volume, не bind mount
- ☐ Настроен
healthcheckдля БД - ☐
depends_onсcondition: service_healthyдля зависимых сервисов - ☐ Образы указаны с явным тегом (
postgres:16, неpostgres:latest) - ☐ Настроен
restart: unless-stoppedдля продакшена - ☐ Проверен конфиг командой
docker compose config - ☐ Сети разделены для изоляции сервисов
- ☐ Настроен бэкап данных из volumes
Заключение
Docker Compose — стандарт для локальной разработки и простых продакшен-стеков. Один файл описывает весь стек, одна команда его поднимает. Это удобно, воспроизводимо и легко переносится между окружениями.
Используйте Docker Compose когда: одна машина, несколько сервисов, нужна простота и скорость.
Переходите на Kubernetes когда: нужна высокая доступность, несколько серверов, сложный auto-scaling или вы уже чувствуете потолок Compose.
Сохраните статью в закладки — команды и примеры пригодятся.



