Перейти к основному содержимому

Docker Compose — готовые стеки


Для кого эта статья

Если вы гуглите docker compose example, docker-compose.yml postgres, как поднять nginx в docker, wordpress docker compose или локальная база данных docker — здесь готовые файлы с разбором каждой строки, как в популярных галереях кода для учёбы. Можно скопировать каталог, выполнить docker compose up -d и сразу увидеть результат в браузере или терминале.

Статья рассчитана на:

  • школьников и студентов на лабораторных по информатике, DevOps и веб-разработке;
  • начинающих, которым нужен шаблон «прямо сейчас», но хочется понять, что означает каждая строка YAML;
  • тех, кто поднимает локальное окружение «как у команды» без десятка ручных команд docker run.

Docker Compose — один текстовый файл (compose.yaml), в котором описано несколько контейнеров сразу: веб-сервер, база, кэш, мониторинг. Одна команда docker compose up поднимает весь набор связанных сервисов на вашем ПК.

Сначала теория — потом копирование

Полный разбор Compose, команд CLI и типичных ошибок — Docker Compose. Основы Docker и образов — Docker. Сборка своего образа — Dockerfile и галерея из 10 типовых Dockerfile. Пошаговый стек Prometheus + Grafana — практикум.

Загрузка Compose…

Симулятор выше показывает, как depends_on с healthcheck откладывает старт приложения, пока PostgreSQL не станет healthy. Тот же приём — в стеках №3–5, 4, 14 ниже.


Три слова — контейнер, образ, compose

ТерминПростыми словамиАналогия
Образ (image)«Чертёж» программы с ОС и файламиУстановочный диск
КонтейнерЗапущенный экземпляр образаОткрытая программа
Compose-файлСписок контейнеров и связей между нимиСценарий «поднять сайт + БД + кэш»
СервисИмя блока в YAML (web, db)Имя участника в сценарии
Volume (том)Место на диске, где данные живут вне контейнераФлешка с файлами БД
Port mapping8080:80 — с вашего ПК на порт внутри контейнераДверь в комнату
Ваш браузер Docker-сеть проекта
localhost:8080 ──ports──► [ web :80 ]

│ DB_HOST=db

[ db :5432 ] ◄── volume pg_data (данные на диске)

Запомните: браузер на Windows/macOS/Linux ходит на localhost + левый порт из ports. Контейнер api к PostgreSQL подключается по имени db — это DNS внутри Docker, не IP и не localhost.


Как читать compose-файл

КлючСмысл простыми словами
servicesСписок контейнеров проекта (web, db, cache…)
imageГотовый образ из registry (nginx:1.27-alpine)
buildСобрать образ из Dockerfile в указанной папке
portsПроброс хост:контейнер — браузер ходит на localhost
environmentПеременные внутри контейнера
env_fileПодтянуть пары ключ=значение из .env
volumesТом или bind-mount — данные переживают пересоздание контейнера
depends_onПорядок старта (без гарантии «БД уже принимает SQL»)
healthcheckCompose ждёт успешной проверки перед service_healthy
networksОтдельные виртуальные сети между сервисами
profilesСервис поднимается только с --profile имя
restartПолитика перезапуска после сбоя или reboot хоста

Два адреса — запомните в первую очередь:

ОткудаКуда подключаться
Браузер на вашем ПКhttp://localhost:8080 — левое число в ports
Контейнер к контейнеруимя сервиса (db, redis), не localhost

Пример: в compose-сервисе api строка DB_HOST=db верна; DB_HOST=localhost укажет на loopback внутри контейнера api, где PostgreSQL не слушает.


Как работать с примерами

  1. Создайте пустую папку, например my-stack/.
  2. Сохраните блок YAML в compose.yaml (или docker-compose.yml — оба имени Compose понимает).
  3. При необходимости добавьте .env и вспомогательные файлы из раздела.
  4. Откройте терминал в этой папке (PowerShell, cmd, bash, WSL — без разницы).
  5. Выполните команды по порядку:
docker compose config
docker compose up -d
docker compose ps

Разбор команд:

КомандаЧто делает
docker compose configЧитает YAML, подставляет .env, печатает итог без запуска контейнеров — ловит опечатки до up
docker compose up -dСкачивает образы (если нет), создаёт сеть и тома, запускает все сервисы; -d = detached, терминал свободен
docker compose psТаблица: имя, статус (Up, healthy), какие порты проброшены на хост
  1. Откройте URL из раздела «Проверка» или смотрите логи: docker compose logs -f web (имя сервиса из YAML).
  2. Остановка без удаления данных БД: docker compose down.
  3. Полный сброс данных в named volumes: docker compose down -v — все таблицы Postgres «как новые».
Docker Desktop на Windows

Перед docker compose up убедитесь, что Docker Desktop запущен (иконка в трее «Running»). Ошибка Cannot connect to the Docker daemon означает, что демон не работает — не ошибка в YAML.

Пароли в примерах

Значения вроде secret и admin подходят только для локального dev. В общий Git пароли не коммитьте — используйте .env.gitignore) или Docker secrets.


Обязательный каркас проекта

Любой стек ниже можно собрать на этом фундаменте:

services:
example:
image: alpine:3.20
command: ["sleep", "infinity"]

# volumes:
# data:
ФрагментСмысл
services:Корневая секция — без неё файл невалиден
example:Имя сервиса; по нему же резолвится DNS внутри сети
image:Образ из Docker Hub или локального кэша
command:Переопределяет CMD образа (здесь контейнер «висит» для отладки)

Что делает файл целиком:

  1. Объявляет один сервис example на базе минимального образа Alpine Linux.
  2. Команда sleep infinity не даёт контейнеру завершиться — удобно для docker compose exec example sh.
  3. Закомментированная секция volumes: — заготовка; раскомментируйте, когда понадобится постоянное хранилище.

Проверка синтаксиса до запуска:

docker compose config

Стартовые стеки

Пять минимальных конфигураций — с одного сервиса до классической связки приложение + БД.

Как устроен каждый пример ниже

Задача — что вы получите и типичный поисковый запрос.
YAML — готовый файл для копирования.
Разбор по строкам — таблица «что означает каждая строка».
Проверка — URL или команда, чтобы убедиться, что стек жив.
Так же устроены популярные галереи вроде Turtle и RegEx — сначала код, потом объяснение.


1. Один nginx — «Hello»

Задача: проверить, что Docker и Compose работают. Частый запрос в поиске: docker compose nginx example, nginx docker compose yml.

services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"

Разбор по строкам:

СтрокаСмысл
services:Начало списка контейнеров проекта
web:Имя сервиса; по нему же обращаются другие контейнеры (http://web)
image: nginx:1.27-alpineОбраз с Docker Hub: веб-сервер nginx на лёгкой Alpine Linux; тег 1.27-alpine фиксирует версию
ports:Секция проброса портов на ваш ПК
- "8080:80"Формат порт_хоста:порт_контейнера. Браузер → localhost:8080 → nginx слушает :80 внутри

Что происходит при docker compose up -d:

  1. Compose создаёт виртуальную сеть проекта (имя = имя папки).
  2. Если образа nginx:1.27-alpine нет — скачивает (pull).
  3. Создаёт контейнер …-web-1, подключает к сети, пробрасывает порт 8080.
  4. Nginx стартует и отдаёт дефолтную страницу «Welcome to nginx».

Проверка: http://localhost:8080 — страница «Welcome to nginx».

Команды и разбор:

docker compose up -d
docker compose logs web
docker compose down
КомандаСмысл
up -dПоднять стек в фоне
logs webПоказать stdout/stderr сервиса web (ошибки конфига nginx видны здесь)
downОстановить и удалить контейнер и сеть; образ nginx на диске остаётся

Если порт 8080 занят — замените на "8888:80" и откройте http://localhost:8888.


2. Статика с диска

Задача: отдавать свой index.html без пересборки образа. Поиск: docker nginx static files, монтировать папку в контейнер.

Структура каталога:

static-site/
compose.yaml
html/
index.html

Пример html/index.html:

<!DOCTYPE html>
<html lang="ru">
<head><meta charset="UTF-8"><title>Мой сайт</title></head>
<body><h1>Привет из Docker!</h1></body>
</html>

compose.yaml:

services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro

Разбор по строкам:

СтрокаСмысл
volumes:Подключение папок или томов к файловой системе контейнера
./html:/usr/share/nginx/html:roBind mount: папка html рядом с YAML → стандартная папка статики nginx; :ro = read-only (контейнер не перезапишет ваши файлы)

Что делает bind mount:

  • Файлы лежат на вашем диске в static-site/html/.
  • Nginx внутри контейнера читает их как /usr/share/nginx/html/index.html.
  • Правка index.html в блокноте → F5 в браузере — без docker compose up заново.

Проверка: измените текст в html/index.html — обновите страницу в браузере.


3. Только PostgreSQL для разработки

Задача: локальная БД на порту 5432 с сохранением данных в томе. Поиск: docker compose postgres, postgresql docker-compose.yml example, поднять postgres локально docker.

services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
ports:
- "5432:5432"
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
timeout: 3s
retries: 5

volumes:
pg_data:

Разбор по строкам:

СтрокаСмысл
db:Имя сервиса; другие контейнеры подключаются к хосту db, порт 5432
postgres:16-alpineОфициальный образ PostgreSQL 16
POSTGRES_USERИмя суперпользователя БД (создаётся при первом старте тома)
POSTGRES_PASSWORDПароль; без него образ не стартует
POSTGRES_DBБаза, которую Postgres создаст при инициализации
"5432:5432"DBeaver, pgAdmin, Python на хосте подключаются к localhost:5432
pg_data:/var/lib/postgresql/dataДанные таблиц хранятся в named volume, не внутри слоя контейнера
healthcheckПериодически запускает pg_isready; статус healthy виден в docker compose ps
interval: 5sПроверка каждые 5 секунд
retries: 5После 5 неудач — сервис unhealthy
volumes: (в корне)Объявление имени тома pg_data; Docker создаст его автоматически

Строка подключения с вашего ПК (не из контейнера):

postgresql://app:secret@localhost:5432/appdb

Что происходит при первом up:

  1. Создаётся пустой том pg_data.
  2. Postgres инициализирует кластер, создаёт пользователя app и БД appdb.
  3. При повторном up после down данные на месте — init не повторяется.

Проверка:

docker compose exec db psql -U app -d appdb -c "SELECT 1;"
Часть командыСмысл
exec dbВыполнить команду внутри работающего контейнера сервиса db
psql -U app -d appdbКлиент SQL: пользователь app, база appdb
-c "SELECT 1;"Один запрос без интерактивной оболочки

Ожидаемый вывод — таблица с числом 1.

Подробнее про Postgres в контейнере — PostgreSQL в Docker.


4. Приложение + PostgreSQL

Задача: API или бэкенд подключается к БД по имени сервиса db. Поиск: docker compose node postgres, spring boot docker compose postgres, backend + database compose.

services:
api:
image: node:20-alpine
working_dir: /app
command: ["node", "server.js"]
volumes:
- ./app:/app
ports:
- "3000:3000"
environment:
DB_HOST: db
DB_PORT: "5432"
DB_USER: app
DB_PASSWORD: secret
DB_NAME: appdb
depends_on:
db:
condition: service_healthy

db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
retries: 5

volumes:
pg_data:

Разбор сервиса api:

СтрокаСмысл
node:20-alpineОбраз Node.js 20 — для учебного примера без своего Dockerfile
working_dir: /appТекущая папка внутри контейнера
command: ["node", "server.js"]Что запустить вместо дефолтного CMD образа
./app:/appВаш код на диске монтируется в контейнер — правки без rebuild
DB_HOST: dbКлючевой момент: хост БД = имя сервиса db, не localhost
depends_on + service_healthyКонтейнер api создаётся только после healthy у db

Почему localhost ломает подключение:

Где выполняется кодКуда указывать DB_HOST
Node/Python внутри контейнера apidb
DBeaver на вашем ПКlocalhost
psql через docker compose exec dbсокет внутри контейнера db

Минимальный app/server.js для проверки (опционально):

const http = require('http');
const { Client } = require('pg');

const client = new Client({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});

client.connect()
.then(() => {
http.createServer((_req, res) => {
res.end('API + Postgres OK');
}).listen(3000);
})
.catch((err) => {
console.error('DB error:', err.message);
process.exit(1);
});

В папке app/ выполните npm init -y && npm install pg на хосте (или добавьте package.json в репозиторий).

Проверка: http://localhost:3000 — текст «API + Postgres OK»; в логах docker compose logs api нет connection refused.


5. Redis рядом с PostgreSQL

Задача: кэш или сессии в Redis, постоянные данные — в Postgres. Поиск: docker compose redis postgres, full stack docker compose.

services:
api:
build: .
ports:
- "3000:3000"
environment:
DB_HOST: db
REDIS_URL: redis://redis:6379/0
depends_on:
db:
condition: service_healthy
redis:
condition: service_started

db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5

redis:
image: redis:7-alpine
volumes:
- redis_data:/data

volumes:
pg_data:
redis_data:

Разбор ключевых строк:

СтрокаСмысл
build: .Compose соберёт образ из Dockerfile в текущей папке (ваше приложение)
REDIS_URL: redis://redis:6379/0URL к Redis: хост redis — имя сервиса; /0 — номер логической БД Redis
service_healthy для dbЖдём готовности SQL
service_started для redisДостаточно запущенного процесса; Redis поднимается быстро
redis_data:/dataПерсистентность Redis (RDB/AOF в томе) — опционально для dev, полезно для учебных данных

Типичное разделение ролей:

СервисЧто хранит
PostgreSQLПользователи, заказы, всё, что нельзя потерять
RedisСессии, кэш страниц, счётчики, очереди на время

Проверка Redis из контейнера api:

docker compose exec redis redis-cli ping

Ответ PONG — сервер жив.


Примеры стеков

Готовые конфигурации под типовые задачи — от CMS до мониторинга и S3-совместимого хранилища.


6. WordPress + MariaDB

Задача: блог или учебный сайт за пару минут. Поиск: wordpress docker compose, wordpress mysql docker-compose.yml.

services:
wordpress:
image: wordpress:6.7-apache
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wp
WORDPRESS_DB_PASSWORD: wppass
WORDPRESS_DB_NAME: wpdb
volumes:
- wp_uploads:/var/www/html/wp-content/uploads
depends_on:
db:
condition: service_healthy

db:
image: mariadb:11
environment:
MARIADB_USER: wp
MARIADB_PASSWORD: wppass
MARIADB_DATABASE: wpdb
MARIADB_ROOT_PASSWORD: rootpass
volumes:
- mariadb_data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5

volumes:
wp_uploads:
mariadb_data:

Разбор по строкам:

СтрокаСмысл
wordpress:6.7-apacheCMS + Apache PHP в одном образе
WORDPRESS_DB_HOST: dbWordPress подключается к MariaDB по имени сервиса
WORDPRESS_DB_*Учётные данные должны совпадать с MARIADB_* у db
wp_uploadsЗагруженные картинки переживают пересоздание контейнера wordpress
mariadb:11Форк MySQL, совместим с WordPress
MARIADB_ROOT_PASSWORDПароль root (для админских задач, не для WP)
healthcheck.shВстроенный скрипт образа MariaDB — ждёт инициализацию InnoDB

Что происходит при первом открытии сайта:

  1. WordPress подключается к MariaDB и создаёт таблицы.
  2. Мастер установки просит язык, заголовок сайта, логин admin.
  3. Данные таблиц — в томе mariadb_data; медиафайлы — в wp_uploads.

Проверка: http://localhost:8080 — мастер установки WordPress.


7. MongoDB + веб-админка

Задача: документная БД и UI для просмотра коллекций. Поиск: mongodb docker compose, mongo-express docker.

services:
mongo:
image: mongo:7
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: secret
volumes:
- mongo_data:/data/db
ports:
- "27017:27017"

mongo-express:
image: mongo-express:1.0.2-20
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: secret
ME_CONFIG_MONGODB_URL: mongodb://root:secret@mongo:27017/
depends_on:
- mongo

volumes:
mongo_data:

Разбор по строкам:

СтрокаСмысл
MONGO_INITDB_ROOT_*Создаёт root-пользователя при первом старте тома
mongo_data:/data/dbФайлы БД на named volume
"27017:27017"Драйвер MongoDB на хосте (Compass, Node, Python) → mongodb://root:secret@localhost:27017
mongo-expressВеб-UI для просмотра коллекций (только dev, не для prod)
ME_CONFIG_MONGODB_URLURL из контейнера mongo-express: хост mongo, не localhost
depends_on: - mongoПорядок старта без healthcheck

Проверка:

URL / строкаНазначение
http://localhost:8081mongo-express в браузере
mongodb://root:secret@localhost:27017Подключение с вашего ПК
mongodb://root:secret@mongo:27017Подключение из другого сервиса в том же compose

8. Prometheus + Grafana (минимум)

Задача: локальный мониторинг метрик. Поиск: prometheus grafana docker compose. Полный стенд с provisioning — в практикуме Prometheus.

Структура каталога:

monitoring/
compose.yaml
prometheus/
prometheus.yml

compose.yaml:

services:
prometheus:
image: prom/prometheus:v2.53.0
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus

grafana:
image: grafana/grafana:12.0.0
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
- grafana_data:/var/lib/grafana
depends_on:
- prometheus

volumes:
prometheus_data:
grafana_data:

Разбор по строкам:

СтрокаСмысл
./prometheus/prometheus.yml:...:roКонфиг scrape с диска; :ro — контейнер не перезапишет файл
prometheus_data:/prometheusИстория метрик (TSDB) сохраняется между перезапусками
GF_SECURITY_ADMIN_*Логин и пароль первого входа в Grafana
grafana_dataДашборды и настройки Grafana
depends_on: prometheusGrafana стартует после Prometheus (без healthcheck)

Минимальный prometheus/prometheus.yml:

global:
scrape_interval: 15s

scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["localhost:9090"]

Разбор prometheus.yml:

СтрокаСмысл
scrape_interval: 15sКак часто опрашивать targets
job_name: prometheusИмя job в UI
targets: ["localhost:9090"]Внутри контейнера prometheus он сам слушает 9090 — здесь localhost корректен
URLНазначение
http://localhost:9090Prometheus UI
http://localhost:3000Grafana (логин admin / admin)

Настройка datasource в Grafana: URL http://prometheus:9090 — Grafana работает в контейнере и видит Prometheus по имени сервиса. localhost:9090 в datasource Grafana не сработает — это был бы loopback самой Grafana.

Готовые PromQL-запросы с разбором — Prometheus + Grafana — запросы: up, rate, CPU, HTTP, P99, алерты.


9. MailHog — перехват писем в dev

Задача: приложение шлёт SMTP на MailHog; письма видны в браузере, реальная почта не уходит. Поиск: mailhog docker compose, тестовая почта docker.

services:
mailhog:
image: mailhog/mailhog:v1.0.1
ports:
- "1025:1025"
- "8025:8025"

Разбор по строкам:

СтрокаСмысл
mailhog/mailhogЛёгкий SMTP-сервер + веб-инbox для разработки
"1025:1025"SMTP — ваше приложение на хосте шлёт на localhost:1025
"8025:8025"HTTP UI — список писем в браузере

Переменные для приложения на хосте:

ПеременнаяЗначение
SMTP_HOSTlocalhost
SMTP_PORT1025
TLS / authне нужны — MailHog принимает всё

Проверка: http://localhost:8025 — пустой inbox; отправьте тестовое письмо из приложения — оно появится в UI.


10. MinIO — S3-совместимое хранилище

Задача: локальный объектный storage для загрузки файлов. Поиск: minio docker compose, s3 local docker.

services:
minio:
image: minio/minio:RELEASE.2024-12-18T13-15-44Z
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
volumes:
- minio_data:/data

volumes:
minio_data:

Разбор по строкам:

СтрокаСмысл
command: server /data ...Запуск сервера MinIO; данные в /data (том minio_data)
--console-address ":9001"Админ-консоль на порту 9001 внутри контейнера
9000S3 API — SDK и aws-cli ходят сюда
9001Веб-консоль — buckets, ключи доступа
MINIO_ROOT_*Root-логин для консоли и API
URLНазначение
http://localhost:9000S3 API endpoint
http://localhost:9001Консоль MinIO (логин minioadmin / minioadmin)

11. Nginx как reverse proxy

Задача: один входной порт, два бэкенда по пути /api и /. Поиск: nginx reverse proxy docker compose, docker compose несколько сервисов один порт.

Структура:

proxy-demo/
compose.yaml
nginx.conf
frontend/
index.html

compose.yaml:

services:
proxy:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- frontend
- api

frontend:
image: nginx:1.27-alpine
volumes:
- ./frontend:/usr/share/nginx/html:ro

api:
image: hashicorp/http-echo:1.0
command: ["-text=API OK"]

nginx.conf (фрагмент):

events {}
http {
upstream front { server frontend:80; }
upstream back { server api:5678; }

server {
listen 80;
location /api/ {
proxy_pass http://back/;
}
location / {
proxy_pass http://front;
}
}
}

Разбор compose:

СервисРоль
proxyЕдинственный с ports — «лицо» стека для браузера
frontendВнутренний nginx со статикой; без проброса портов наружу
apiУчебный HTTP-сервер; слушает 5678 (дефолт http-echo)

Разбор nginx.conf:

СтрокаСмысл
upstream front &#123; server frontend:80; &#125;Имя frontend — DNS в Docker-сети
location /api/Запросы с префиксом /api/ → бэкенд
proxy_pass http://back/;Проксирование на upstream back (http-echo)
location /Всё остальное → статика

Проверка:

URLОжидание
http://localhost:8080/Ваш frontend/index.html
http://localhost:8080/api/Текст API OK

Подробнее про конфиги nginx — Nginx — конфиги под задачу.


12. Профили dev и tools

Задача: БД поднимается всегда; pgAdmin — только когда нужен. Поиск: docker compose profiles, pgadmin docker compose.

services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- pg_data:/var/lib/postgresql/data

pgadmin:
profiles: ["tools"]
image: dpage/pgadmin4:8
ports:
- "5050:80"
environment:
PGADMIN_DEFAULT_EMAIL: admin@local.dev
PGADMIN_DEFAULT_PASSWORD: admin
depends_on:
- db

volumes:
pg_data:

Разбор по строкам:

СтрокаСмысл
profiles: ["tools"]Сервис не стартует при обычном docker compose up
PGADMIN_DEFAULT_EMAILЛогин в UI pgAdmin
5050:80UI pgAdmin → http://localhost:5050

Запуск и разбор команд:

docker compose up -d
docker compose --profile tools up -d pgadmin
КомандаСмысл
up -dТолько db — pgAdmin пропущен
--profile toolsВключить сервисы с профилем tools
up -d pgadminПоднять только pgAdmin (db уже работает)

Подключение в pgAdmin: host db, port 5432, user/password из compose Postgres (создайте server в UI).


13. Переменные из .env

Задача: порты и пароли в одном файле, не захардкожены в YAML. Поиск: docker compose env file, docker-compose .env example.

.env:

APP_PORT=3000
POSTGRES_PASSWORD=secret
POSTGRES_USER=app
POSTGRES_DB=appdb

compose.yaml:

services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "5432:5432"
volumes:
- pg_data:/var/lib/postgresql/data

volumes:
pg_data:

Разбор:

Файл / строкаСмысл
.envЛежит рядом с compose; Compose подставляет значения автоматически
`${POSTGRES_USER}`Плейсхолдер — заменится на app из .env
.gitignoreФайл .env не коммитьте; в репозиторий — .env.example без секретов

Что делает docker compose config:

  1. Читает compose.yaml.
  2. Подставляет переменные из .env и из окружения ОС.
  3. Печатает итоговый YAML — удобно проверить, что пароль подставился.

Шаблон .env.example для README:

POSTGRES_USER=app
POSTGRES_PASSWORD=change_me
POSTGRES_DB=appdb

14. Стек для CI-тестов

Задача: поднять БД, прогнать тесты в одноразовом контейнере, остановить всё. Поиск: docker compose run test, ci postgres docker compose.

services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: test
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 3s
retries: 10

test:
build: .
environment:
DATABASE_URL: postgres://postgres:test@db:5432/postgres
depends_on:
db:
condition: service_healthy
command: ["npm", "test"]

Разбор по строкам:

СтрокаСмысл
db без portsБД только внутри Docker-сети — с хоста порт не нужен
DATABASE_URLСтандартная строка для ORM; хост db, пароль test
command: ["npm", "test"]Переопределяет CMD образа — запуск тестов
retries: 10В CI БД иногда дольше стартует — больше попыток healthcheck

Типичный сценарий в пайплайне:

docker compose up -d db
docker compose run --rm test
docker compose down -v

Разбор команд:

КомандаСмысл
up -d dbПоднять только сервис db, не test
run --rm testСоздать контейнер test, выполнить npm test, удалить контейнер после exit
down -vОчистить тома — следующий прогон CI с чистой БД

Отличие run от up: up держит сервисы постоянно; run — одноразовая команда (миграции, тесты, seed).


Шпаргалка команд

ЗадачаКомандаЧто происходит под капотом
Поднять стекdocker compose up -dpull/build → сеть → тома → контейнеры → healthcheck
Пересобрать образыdocker compose up -d --builddocker build для каждого build: перед стартом
Статусdocker compose psИмена контейнеров, Up (healthy), проброшенные порты
Логиdocker compose logs -f apistdout/stderr; -f = поток, как tail -f
Shell в контейнереdocker compose exec db shНовый процесс внутри уже работающего контейнера
Одноразовая командаdocker compose run --rm test npm testНовый контейнер, команда, удаление после exit
Проверка YAMLdocker compose configMerge файлов + подстановка .env, без изменений на хосте
Остановкаdocker compose downStop + remove контейнеров и сети; тома остаются
Сброс данныхdocker compose down -vУдаляет named volumes — БД «с нуля»
Только один сервисdocker compose up -d dbОстальные сервисы не трогаются

Частые вопросы из поиска

ВопросКороткий ответГде пример
Как поднять PostgreSQL локально?compose.yaml + docker compose up -dстек №3
docker-compose.yml nginxОдин сервис web, ports: "8080:80"стек №1
WordPress docker composewordpress + mariadb + volumesстек №6
app + postgres + redisтри сервиса, DB_HOST=db, REDIS_URL=redis://redis:6379стек №5
Cannot connect to Docker daemonЗапустите Docker Desktop / systemctl start dockerраздел «Как работать»
connection refused к БДВ контейнере app — хост db, не localhostстек №4
Куда делись данные после down?Нужен named volume; down -v удаляет томастек №3
version: '3' obsoleteВ Compose V2 ключ version: часто опускаюттеория

Типичные ошибки новичка

ОшибкаЧто происходитКак исправить
DB_HOST=localhost в сервисе appAPI не видит Postgresуказать имя сервиса db
Нет healthcheck, только depends_onapp падает при старте БДcondition: service_healthy + pg_isready
Забыли том для БДданные пропали после downnamed volume на /var/lib/postgresql/data
Порт занят на хостеBind for 0.0.0.0:5432 failedсменить левую часть в ports, например 15432:5432
.env в Gitутечка паролей.gitignore + пример .env.example
docker compose down -v на учебной БДвсе таблицы «с нуля»бэкап или без -v

Практика — задания для лабораторной

  1. Nginx. Поднимите стек №1, замените порт 8080 на 8888, выполните docker compose ps — в колонке PORTS должно быть 8888->80/tcp.
  2. Postgres. Подключитесь к стеку №3 через DBeaver или psql: host localhost, port 5432, user app, password secret, database appdb.
  3. Сеть. В стеке №4 выполните docker compose exec api getent hosts db — в ответе IP контейнера db.
  4. Том. Создайте таблицу в Postgres, выполните docker compose down и снова up -d — таблица на месте. Затем down -v и up -d — таблицы нет (данные сброшены).
  5. Профиль. В стеке №12 убедитесь, что после up -d pgAdmin не запущен (docker compose ps), а после --profile tools — появился.

Разбор команд и архитектуры — в Docker Compose.


Оглавление стеков — быстрый выбор

СтекКогда брать
1nginxПроверка Docker, первая лабораторная
2nginx + папка htmlСтатический сайт, фронт без сборки
3PostgreSQLЛокальная SQL-база для Java, Python, C#
4Node + PostgreSQLBackend + БД, учебный fullstack
5app + Postgres + RedisКэш, сессии, «как в проде»
6WordPress + MariaDBCMS, блог, курсовой сайт
7MongoDB + mongo-expressNoSQL, документы JSON
8Prometheus + GrafanaМетрики, мониторинг
9MailHogТест email без реальной отправки
10MinIOФайлы, S3 API
11nginx reverse proxyОдин порт, несколько бэкендов
12Postgres + pgAdmin (profile)БД всегда, GUI по необходимости
13.env + PostgresСекреты вне YAML
14db + test (CI)GitHub Actions, GitLab CI

Чек-лист перед сдачей лабораторной

  • docker compose config проходит без ошибок.
  • Для связи между сервисами используются имена сервисов, не localhost.
  • У PostgreSQL/MySQL/MariaDB есть named volume.
  • Пароли вынесены в .env или secrets, не в публичный репозиторий.
  • В README указаны URL, логины и команды up / down.

Связанные материалы

МатериалЗачем открыть
Docker Composeтеория, healthcheck, secrets, CI
Dockerобразы, тома, сеть
Dockerfilebuild: в compose
Dockerfile — 10 типовых образовготовые Dockerfile под Node, Python, Go…
Манифесты зависимостейcompose в структуре проекта
Шаблоныминимальный Dockerfile и прочие каркасы
Практикум Prometheusполный observability-стек
PostgreSQL в Dockerinit-скрипты и PGDATA
Nginx — конфиги под задачуproxy, SPA, PHP-FPM, TLS вне compose

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").