Docker Compose описывает несколько контейнеров в одном файле compose.yaml: сервисы, порты, тома, сеть и переменные окружения — и поднимает их командой docker compose up. Чтобы API не стартовал раньше базы, у PostgreSQL и Redis задают healthcheck, а у API — depends_on с condition: service_healthy. Секреты берутся из .env, данные живут в именованных томах и переживают docker compose down, если не добавить -v.
После материала вы сможете
- описать в
compose.yamlAPI, PostgreSQL и Redis с портами, именованными томами и политикой перезапуска - объяснить, как сервисы находят друг друга по именам во внутренней сети и чем порт контейнера отличается от порта хоста
- передать настройки через
.envс интерполяцией${…}, значениями по умолчанию и обязательными переменными и проверить итогdocker compose config - настроить
healthcheckиdepends_onсservice_healthyиservice_completed_successfully, вынести dev-настройки в override и профили
Что знать заранее
Проверено на
Docker Compose 5.3 (Docker Desktop, Docker Engine 29.6)PostgreSQL 17.11 (postgres:17-alpine)Redis 8.10 (redis:8.10-alpine)Node.js 24.21 (node:24.21.0-alpine)Express 5.3.0pg 8.23.1node-redis 6.3.0
Сверено с документацией: 11 октября 2026 г.
Содержание
Зачем Compose: три контейнера одной командой
В уроке «Docker с нуля» мы запускали один контейнер командой docker run с портом, переменными и томом. Реальному API нужны ещё база и кэш, а значит — три длинные команды, общая сеть, тома, пароли и правильный порядок запуска. Compose описывает всё это декларативно в compose.yaml, и окружение поднимается одинаково у каждого разработчика.
- 01curl / браузер
- 02api :3000
- 03db: PostgreSQL :5432
- 04cache: Redis :6379
Проект
- Что это в Compose
- Всё, что описано в одном
compose.yaml; имя по умолчанию — папка, у насnotes
Сервис
- Что это в Compose
- Описание контейнера: образ или сборка, порты, переменные, тома;
api,db,cache
Контейнер
- Что это в Compose
- Запущенный экземпляр сервиса:
notes-api-1
Сеть
- Что это в Compose
- Создаётся автоматически (
notes_default), сервисы находят друг друга по имени
Том
- Что это в Compose
- Именованное хранилище данных проекта:
notes_pgdata,notes_redisdata
Compose входит в Docker Desktop, на Linux ставится как плагин docker compose. Проверено на Docker Compose 5.3 с Docker Engine 29.6. Поле version в начале файла больше не нужно: по документации оно только информационное, и Compose предупреждает, что оно устарело.
Приложение: API с PostgreSQL и Redis
Возьмём API заметок из урока о Docker и подключим к нему настоящие хранилища: заметки — в PostgreSQL, список заметок кэшируется в Redis на 30 секунд по схеме cache-aside. /health проверяет и базу, и Redis — его будет вызывать HEALTHCHECK контейнера. Адреса хранилищ приходят из переменных DATABASE_URL и REDIS_URL.
mkdir notes && cd notes
npm init -y && npm pkg set type=module
npm install express@5.3.0 pg@8.23.1 redis@6.3.0
npm install -D typescript@7.0.2 @types/express@5.0.6 @types/node@24.19.2 @types/pg@8.23.1import express from "express";
import pg from "pg";
import { createClient } from "redis";
const port = Number(process.env.PORT ?? 3000);
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const redis = createClient({ url: process.env.REDIS_URL });
redis.on("error", (err) => console.error("redis error:", err.message));
await redis.connect();
const CACHE_KEY = "notes:all";
const app = express();
app.use(express.json());
app.get("/health", async (_req, res) => {
try {
await pool.query("SELECT 1");
await redis.ping();
res.json({ status: "ok", db: "ok", redis: "ok" });
} catch (err) {
res.status(503).json({ status: "error", message: (err as Error).message });
}
});
// Cache-aside: read from Redis, on a miss read from PostgreSQL and cache for 30 s
app.get("/api/notes", async (_req, res) => {
const cached = await redis.get(CACHE_KEY);
if (cached) {
res.set("X-Cache", "HIT").type("json").send(cached);
return;
}
const { rows } = await pool.query("SELECT id, text, created_at FROM notes ORDER BY id");
await redis.set(CACHE_KEY, JSON.stringify(rows), { EX: 30 });
res.set("X-Cache", "MISS").json(rows);
});
app.post("/api/notes", async (req, res) => {
const text = typeof req.body?.text === "string" ? req.body.text.trim() : "";
if (!text) {
res.status(400).json({ error: "text is required" });
return;
}
const { rows } = await pool.query(
"INSERT INTO notes (text) VALUES ($1) RETURNING id, text, created_at",
[text],
);
await redis.del(CACHE_KEY);
res.status(201).json(rows[0]);
});
const server = app.listen(port, () => {
console.log(`notes-api listening on port ${port}`);
});
process.on("SIGTERM", () => {
console.log("SIGTERM received, shutting down");
server.close(async () => {
await pool.end();
await redis.close();
process.exit(0);
});
});tsc без ошибок, работа в контейнере с PostgreSQL 17.11 и Redis 8.10.-- Runs once, when the PostgreSQL data volume is empty
CREATE TABLE notes (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
text text NOT NULL CHECK (length(text) BETWEEN 1 AND 500),
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO notes (text) VALUES ('Hello from init.sql');/docker-entrypoint-initdb.d образ postgres выполняет только при пустом каталоге данных — то есть при первом запуске с новым томом.Dockerfile — тот же многоэтапный из урока «Docker с нуля», без двух строк про папку данных (DATA_DIR и /app/data): файлы приложение больше не пишет. Его HEALTHCHECK вызывает /health, поэтому контейнер API станет healthy только когда доступны и база, и Redis.
compose.yaml: сервисы, порты, тома
services:
api:
build: .
ports:
- "127.0.0.1:${API_PORT:-8080}:3000"
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
restart: true
cache:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:17-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER in .env}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB in .env}
volumes:
- pgdata:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
restart: unless-stopped
cache:
image: redis:8.10-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
restart: unless-stopped
psql:
image: postgres:17-alpine
profiles: ["tools"]
environment:
PGHOST: db
PGUSER: ${POSTGRES_USER}
PGPASSWORD: ${POSTGRES_PASSWORD}
PGDATABASE: ${POSTGRES_DB}
entrypoint: ["psql"]
depends_on:
db:
condition: service_healthy
volumes:
pgdata:
redisdata:docker compose up -d --wait — все три сервиса healthy.build: .
- Что делает
- Собрать образ из Dockerfile в папке проекта; имя образа —
notes-api
image: postgres:17-alpine
- Что делает
- Готовый образ из реестра; версию закрепляем, как и в уроке «PostgreSQL: установка в Docker»
ports: "127.0.0.1:${API_PORT:-8080}:3000"
- Что делает
- Порт хоста → порт контейнера; без
127.0.0.1порт открыт на всех интерфейсах
volumes: pgdata:/var/lib/postgresql/data
- Что делает
- Именованный том для данных; для PostgreSQL 17 путь именно такой, у 18 —
/var/lib/postgresql
./db/init.sql:…:ro
- Что делает
- Файл с хоста (bind mount) только для чтения
command:
- Что делает
- Заменяет
CMDобраза: Redis с журналом AOF (--appendonly yes), чтобы данные переживали перезапуск
restart: unless-stopped
- Что делает
- Перезапускать после сбоя и перезагрузки Docker, если контейнер не остановили вручную
volumes: в конце файла
- Что делает
- Объявление именованных томов проекта
Сеть и имена сервисов
Compose создаёт для проекта сеть <проект>_default и подключает к ней все сервисы. Внутри сети имя сервиса работает как имя хоста: поэтому в DATABASE_URL написано @db:5432, а в REDIS_URL — cache:6379. Сервисы общаются по портам контейнеров; порт хоста (8080, 55442) нужен только для подключения снаружи, с вашей машины.
$ curl -s http://127.0.0.1:8080/health
{"status":"ok","db":"ok","redis":"ok"}
$ curl -si http://127.0.0.1:8080/api/notes | grep -E "^X-Cache|^\["
X-Cache: MISS
[{"id":"1","text":"Hello from init.sql","created_at":"2026-10-12T02:08:11.573Z"}]
$ curl -si http://127.0.0.1:8080/api/notes | grep -E "^X-Cache|^\["
X-Cache: HIT
[{"id":"1","text":"Hello from init.sql","created_at":"2026-10-12T02:08:11.573Z"}]
$ curl -s -X POST http://127.0.0.1:8080/api/notes -H "Content-Type: application/json" -d '{"text":"Order coffee beans"}'
{"id":"2","text":"Order coffee beans","created_at":"2026-10-12T02:08:29.159Z"}
$ curl -si http://127.0.0.1:8080/api/notes | grep -E "^X-Cache|^\["
X-Cache: MISS
[{"id":"1","text":"Hello from init.sql","created_at":"2026-10-12T02:08:11.573Z"},{"id":"2","text":"Order coffee beans","created_at":"2026-10-12T02:08:29.159Z"}]POST кэш сброшен. Реальный вывод.$ docker compose exec api node -e "require('node:dns').promises.lookup('db').then(a => console.log('db ->', a.address))"
db -> 192.168.0.3
$ docker network ls --filter name=notes
NETWORK ID NAME DRIVER SCOPE
392a30dbb8d0 notes_default bridge localdb внутри сети разрешается в адрес контейнера; сеть называется по проекту. В проверке у проекта был префикс kb-, в статье он убран.idпришёл строкой ("1"): типbigintдрайверpgпо умолчанию отдаёт строкой, чтобы не потерять точность больших чисел в JavaScriptlocalhostвнутри контейнера — это сам контейнер. Ошибка «API не видит базу» чаще всего —localhostвместо имени сервиса в строке подключения- Сеть можно описать явно в разделе
networks— например, чтобы отделить базу от публичных сервисов. Для одного проекта хватает сети по умолчанию
Переменные окружения и .env
Файл .env рядом с compose.yaml Compose читает автоматически и подставляет значения в выражения ${…} в самом файле. Это интерполяция: значения попадают в контейнер, только если вы передали их через environment. В git кладут .env.example без секретов, а сам .env — в .gitignore.
# Local development only. Copy from .env.example; never commit this file
POSTGRES_USER=notes
POSTGRES_PASSWORD=change-me-locally
POSTGRES_DB=notes
API_PORT=8080@, : или /, в DATABASE_URL его нужно закодировать (URL-encoding).POSTGRES_USER=notes
POSTGRES_PASSWORD=
POSTGRES_DB=notes
API_PORT=8080${POSTGRES_USER}
- Значение
- Значение переменной; если её нет — пустая строка и предупреждение
${API_PORT:-8080}
- Значение
- Значение или
8080, если переменная не задана или пустая
${POSTGRES_PASSWORD:?текст}
- Значение
- Остановиться с ошибкой и текстом, если переменная не задана или пустая
$${POSTGRES_USER}
- Значение
- Двойной
$— не подставлять: переменную раскроет оболочка внутри контейнера
$ docker compose config --services
cache
db
api
$ docker compose --profile tools config --services
cache
db
api
psql
$ docker compose config | grep -E "DATABASE_URL|POSTGRES_PASSWORD"
DATABASE_URL: postgresql://notes:change-me-locally@db:5432/notes
POSTGRES_PASSWORD: change-me-locally
$ mv .env .env.bak; docker compose config > /dev/null; mv .env.bak .env
time="2026-10-11T20:07:56-06:00" level=warning msg="The \"POSTGRES_USER\" variable is not set. Defaulting to a blank string."
time="2026-10-11T20:07:56-06:00" level=warning msg="The \"POSTGRES_PASSWORD\" variable is not set. Defaulting to a blank string."
time="2026-10-11T20:07:56-06:00" level=warning msg="The \"POSTGRES_DB\" variable is not set. Defaulting to a blank string."
error while interpolating services.db.environment.POSTGRES_USER: required variable POSTGRES_USER is missing a value: set POSTGRES_USER in .envdocker compose config печатает итоговую конфигурацию после подстановки — первое, что стоит смотреть при странном поведении. Без .env срабатывает :?.Приоритет источников: переменная оболочки сильнее файла из --env-file, а тот сильнее .env. Поэтому порт можно поменять на один запуск, не трогая файлы:
$ DB_PORT=55443 docker compose config | grep published
published: "8080"
published: "56392"
published: "55443"DB_PORT из командной строки перекрыл значение из .env.healthcheck и depends_on: порядок запуска
Compose всегда запускает и останавливает контейнеры в порядке зависимостей, но по умолчанию ждёт только, пока зависимость запущена, а не пока она готова. PostgreSQL после старта контейнера ещё несколько секунд инициализирует базу. Поэтому у db и cache есть healthcheck (pg_isready и redis-cli ping), а api зависит от них с condition: service_healthy. Флаг --wait заставляет саму команду дождаться, пока все сервисы станут healthy.
$ docker compose up -d --wait
Image notes-api Building
Image notes-api Built
Network notes_default Creating
Network notes_default Created
Volume notes_redisdata Creating
Volume notes_redisdata Created
Volume notes_pgdata Creating
Volume notes_pgdata Created
Container notes-cache-1 Creating
Container notes-db-1 Creating
Container notes-cache-1 Created
Container notes-db-1 Created
Container notes-api-1 Creating
Container notes-api-1 Created
Container notes-cache-1 Starting
Container notes-db-1 Starting
Container notes-cache-1 Started
Container notes-db-1 Started
Container notes-cache-1 Waiting
Container notes-db-1 Waiting
Container notes-cache-1 Healthy
Container notes-db-1 Healthy
Container notes-api-1 Starting
Container notes-api-1 Started
Container notes-db-1 Waiting
Container notes-cache-1 Waiting
Container notes-api-1 Waiting
Container notes-db-1 Healthy
Container notes-cache-1 Healthy
Container notes-api-1 Healthy
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
notes-api-1 notes-api "docker-entrypoint.s…" api 12 seconds ago Up 5 seconds (healthy) 127.0.0.1:8080->3000/tcp
notes-cache-1 redis:8.10-alpine "docker-entrypoint.s…" cache 12 seconds ago Up 11 seconds (healthy) 127.0.0.1:56392->6379/tcp
notes-db-1 postgres:17-alpine "docker-entrypoint.s…" db 12 seconds ago Up 11 seconds (healthy) 127.0.0.1:55442->5432/tcp
$ curl -s http://127.0.0.1:8080/health
{"status":"ok","db":"ok","redis":"ok"}cache и db до Healthy, только потом api. Строки сборки образа опущены.Что будет с короткой записью depends_on: [db, cache]: мы запустили тот же проект с ней и опрашивали /health раз в полсекунды.
$ docker compose -f compose.short.yaml up -d
Network notes_default Creating
Network notes_default Created
Volume notes_pgdata Creating
Volume notes_pgdata Created
Volume notes_redisdata Creating
Volume notes_redisdata Created
Container notes-db-1 Creating
Container notes-cache-1 Creating
Container notes-cache-1 Created
Container notes-db-1 Created
Container notes-api-1 Creating
Container notes-api-1 Created
Container notes-cache-1 Starting
Container notes-db-1 Starting
Container notes-cache-1 Started
Container notes-db-1 Started
Container notes-api-1 Starting
Container notes-api-1 Started
$ for i in 1 2 3 4 5 6; do curl -s -o /dev/null -w "%{http_code} " http://127.0.0.1:8080/health; sleep 0.5; done; echo
000 503 200 200 200 200
$ docker compose -f compose.short.yaml logs api
api-1 | notes-api listening on port 3000
$ docker compose -f compose.short.yaml ps --format 'table {{.Service}}\t{{.Status}}'
SERVICE STATUS
api Up 4 seconds (health: starting)
cache Up 4 seconds (health: starting)
db Up 4 seconds (health: starting)000 — API ещё не слушает порт, 503 — API запущен, а база ещё не готова; через секунду всё работает. На медленной машине или с миграциями окно ошибок больше.service_started
- Когда стартует зависимый сервис
- Когда контейнер зависимости запущен (так работает короткая запись)
service_healthy
- Когда стартует зависимый сервис
- Когда healthcheck зависимости прошёл
service_completed_successfully
- Когда стартует зависимый сервис
- Когда зависимость завершилась с кодом 0 — для миграций и подготовки данных
restart: true в depends_on перезапускает API, когда Compose перезапускает или обновляет базу:
$ docker compose restart db
Container notes-db-1 Restarting
Container notes-db-1 Started
Container notes-db-1 Waiting
Container notes-db-1 Healthy
Container notes-api-1 Restarting
Container notes-api-1 Started
$ docker compose ps --format 'table {{.Service}}\t{{.Status}}'
SERVICE STATUS
api Up Less than a second (health: starting)
cache Up 36 seconds (healthy)
db Up 6 seconds (healthy)
$ docker compose logs api --tail 3
api-1 | notes-api listening on port 3000
api-1 | SIGTERM received, shutting down
api-1 | notes-api listening on port 3000
$ curl -s http://127.0.0.1:8080/health
{"status":"ok","db":"ok","redis":"ok"}restart db Compose дождался Healthy и перезапустил api; в логе видно корректное завершение по SIGTERM.Профили и override для разработки
Кроме compose.yaml, Compose автоматически читает compose.override.yaml из той же папки и объединяет их: списки портов складываются, переменные объединяются по имени, одиночные значения из второго файла заменяют первые. Туда удобно вынести то, что нужно только разработчику, — например, порты базы и Redis для GUI-клиента.
# Loaded automatically by "docker compose up": local development extras
services:
db:
ports:
- "127.0.0.1:${DB_PORT:-5432}:5432"
cache:
ports:
- "127.0.0.1:${REDIS_PORT:-6379}:6379"DB_PORT/REDIS_PORT в .env.$ docker compose config | grep -E "^ [a-z]+:$|published"
api:
published: "8080"
cache:
published: "56392"
db:
published: "55442"
default:
pgdata:
redisdata:
$ docker compose -f compose.yaml config | grep -E "^ [a-z]+:$|published"
api:
published: "8080"
cache:
db:
default:
pgdata:
redisdata:-f compose.yaml — только API. Так запускают окружение без dev-добавок.Профили включают сервисы по запросу. Сервис без profiles запускается всегда, с профилем — только если профиль активен (--profile tools или COMPOSE_PROFILES=tools) или сервис вызван явно. У нас так описан psql — клиент базы, который не нужен в обычной работе:
$ docker compose run --rm psql -c "\dt"
Container notes-db-1 Running
Container notes-db-1 Waiting
Container notes-db-1 Healthy
Container notes-psql-run-fa3cc8af018b Creating
Container notes-psql-run-fa3cc8af018b Created
List of relations
Schema | Name | Type | Owner
--------+-------+-------+-------
public | notes | table | notes
(1 row)run --rm создаёт одноразовый контейнер сервиса, дожидается здоровой базы и удаляет контейнер после выхода.$ docker compose up -d --wait
Image notes-api Building
…
Container notes-db-1 Starting
Container notes-cache-1 Starting
Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint notes-cache-1 (bc682bd84ba0…): Bind for 0.0.0.0:6379 failed: port is already allocated
$ printf 'DB_PORT=55442\nREDIS_PORT=56392\n' >> .env
$ docker compose up -d --wait
Container notes-cache-1 Recreate
Container notes-db-1 Recreate
…
Container notes-api-1 Healthy…).Команды на каждый день и упражнения
docker compose up -d --wait
- Что делает
- Собрать при необходимости, запустить в фоне и дождаться
healthy
docker compose up -d --build
- Что делает
- Пересобрать образы перед запуском — после изменения кода
docker compose ps
- Что делает
- Контейнеры проекта и их статус
docker compose logs -f api
- Что делает
- Логи сервиса в реальном времени; без имени — всех сервисов
docker compose exec db psql -U notes
- Что делает
- Команда в работающем контейнере сервиса
docker compose run --rm psql -c …
- Что делает
- Одноразовый контейнер сервиса
docker compose restart db
- Что делает
- Перезапустить сервис
docker compose down
- Что делает
- Удалить контейнеры и сеть; тома остаются
docker compose down -v
- Что делает
- То же плюс тома проекта — данные будут потеряны
$ docker compose down
…
Container notes-api-1 Removed
…
Container notes-db-1 Removed
…
Container notes-cache-1 Removed
…
Network notes_default Removed
$ docker volume ls --filter name=notes
DRIVER VOLUME NAME
local notes_pgdata
local notes_redisdata
$ docker compose up -d --wait
…
Container notes-cache-1 Healthy
Container notes-db-1 Healthy
…
Container notes-cache-1 Healthy
Container notes-db-1 Healthy
Container notes-api-1 Healthy
$ docker compose logs db | grep -iE "skipping|init.sql"
db-1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
$ curl -s http://127.0.0.1:8080/api/notes
[{"id":"1","text":"Hello from init.sql","created_at":"2026-10-12T02:08:11.573Z"},{"id":"2","text":"Order coffee beans","created_at":"2026-10-12T02:08:29.159Z"}]
$ docker compose down -v
…
Container notes-api-1 Removed
…
Container notes-db-1 Removed
…
Container notes-cache-1 Removed
…
Volume notes_pgdata Removed
Volume notes_redisdata Removed
Network notes_default Removed
$ docker volume ls --filter name=notes
DRIVER VOLUME NAMEdown сохранил тома — после up база не инициализировалась заново и заметки на месте; down -v удалил тома. Реальный вывод, однотипные строки прогресса свёрнуты (…).Дальше — секреты и переменные окружения в продакшене и деплой: как этот же проект запускается на сервере, где .env с паролями быть не должно.
Как проверено11 · пройденосверено 11 октября 2026 г.
- запущено, работает
src/server.ts (Express 5.3.0, pg 8.23.1, node-redis 6.3.0), установка зависимостей командами из статьи
npm init/npm pkg set/npm install в чистой папке, tsc --noEmit (TypeScript 7.0.2) без ошибок; redis.close() вместо устаревшего quit() (по d.ts node-redis 6.3.0)
- запущено, работает
db/init.sql, compose.yaml, compose.override.yaml, .env, .env.example
Docker Compose 5.3.1 + Docker Engine 29.6.2 (Docker Desktop): docker compose up -d --wait с нуля — cache и db Healthy, затем api Healthy; /health 200; init.sql выполнен (лог entrypoint). Dockerfile — из урока docker-osnovy без строк DATA_DIR и /app/data
- запущено, работает
config: --services, --profile tools, интерполяция, ошибка :? без .env и без POSTGRES_PASSWORD, приоритет переменной оболочки (DB_PORT=55443)
реальный вывод docker compose config; в статье команда grep сокращена до DATABASE_URL|POSTGRES_PASSWORD (в прогоне шаблон включал ещё 127.0.0.1 — те же строки плюс host_ip)
- запущено, работает
конфликт порта 6379 при первом up и исправление через DB_PORT/REDIS_PORT в .env
на машине проверки 5432 и 6379 заняты чужими контейнерами: Bind for 0.0.0.0:6379 failed: port is already allocated; после добавления портов up пересоздал контейнеры. ID эндпоинта в статье сокращён
- запущено, работает
кэш X-Cache MISS/HIT/инвалидация, redis-cli TTL, exec psql, run --rm psql (профиль tools), DNS-имя db, docker network ls, logs
реальный вывод; id bigint возвращается строкой (поведение pg по умолчанию)
- запущено, работает
короткая запись depends_on
копия compose.yaml с depends_on: [db, cache] и пустыми томами: /health раз в 0.5 с — 000 503 200 200 200 200
- запущено, работает
restart db с restart: true, down (тома сохранились, Skipping initialization), down -v
api перезапущен после Healthy базы, в логе SIGTERM received; после down/up заметки на месте; после down -v томов нет. Однотипные строки прогресса в статье свёрнуты в «…»
- запущено, работает
override: config с override и с -f compose.yaml
с override опубликованы 8080/56392/55442, с -f compose.yaml — только 8080
- запущено, работает
упражнение modify (POSTGRES_PASSWORD без значения)
required variable POSTGRES_PASSWORD is missing a value: set POSTGRES_PASSWORD in .env
- запущено, работает
упражнение apply (сервис migrate + service_completed_successfully)
полный compose.yaml с фрагментом из решения: при чистом запуске migrate Exited (0) до старта api, \d notes показывает pinned, повторный up — NOTICE … skipping, --wait exit 0
- запущено, работает
упражнение repeat
шаги совпадают с проверенным основным сценарием
«Запущено» — код выполнен во временном проекте или контейнере, вывод в статье взят из прогона. «Частично» — проверено с заглушками вместо внешних сервисов. «Не запускалось» — нужны устройство, аккаунт или ключ; шаги сверены с официальной документацией. Файлы проверочного проекта и логи запусков сохранены в архиве InterLAB.