Один docker run — это одна команда на один контейнер. Как только сервисов больше одного и они должны видеть друг друга по сети (приложение + БД + кэш) — проще описать всё в одном файле, чем помнить порядок docker run и вручную создавать сеть.

Современный docker compose — встроенная подкоманда Docker (плагин), не отдельный бинарник docker-compose через дефис, как было раньше. Минимальный compose.yaml:

services:
  app:
    image: myapp:latest
    ports:
      - "8080:8080"
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Все сервисы одного файла автоматически попадают в общую сеть — app обращается к базе просто по имени сервиса db, без ручной настройки DNS или --link.

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

docker compose up -d
docker compose down

up -d — поднять всё в фоне, down — остановить и удалить контейнеры (тома с volumes: остаются, если не добавить -v).

Смотреть логи конкретного сервиса, а не грепать общий docker logs:

docker compose logs -f db

Важная тонкость с depends_on: он задаёт только порядок запуска контейнера, не готовность приложения внутри. db стартует раньше app, но PostgreSQL внутри него может ещё не принимать соединения в момент, когда app уже запустился и попытался подключиться. Для реальной готовности нужен healthcheck в описании сервиса db и depends_on: db: condition: service_healthy у app — иначе на первом старте app может упасть с connection refused и разбираться, что не так, придётся уже постфактум.

#docker