0% прочитано

Чому Docker Compose запустив більше сервісів, ніж я очікувала

Команда для одного сервісу може торкнутися його залежностей, якщо фактична Compose-конфігурація пов’язує їх між собою. Розбираю реальний Laradock-кейс із кількома Compose-файлами, PHP 8.3/8.4/8.5, workspace та конфліктом порту.

2 листопада 2026 р. 10 хв читанняLaradock

Після оновлення локального Laradock я хотіла повернути один конкретний сервіс. Команда виглядала точково: запустити Nginx, який уже існував як контейнер.

Але Compose почав запускати також workspace-84 і workspace-85, а workspace для PHP 8.3 не зміг стартувати через зайнятий порт 5173. У результаті проста на вигляд команда торкнулася значно більшої частини середовища, ніж я очікувала.

Проблема була не в Laravel і не в самому Nginx. Щоб зрозуміти поведінку, потрібно було подивитися на фактичну Compose-конфігурацію, набір Compose-файлів, залежності сервісів і поточний стан контейнерів.

Одна команда Compose працює з об’єднаною конфігурацією

У Laradock конфігурація може складатися не з одного файлу. Базовий Compose-файл доповнюється іншими файлами, а змінна COMPOSE_FILE може визначати їхній набір за замовчуванням.

Тому команда docker compose start nginx працює не з тим фрагментом конфігурації, який я пам’ятаю, а з тією моделлю, яку Compose реально зібрав у цей момент.

Перевіряємо набір Compose-файлів і валідність конфігурації
cd ~/Projects/laradock || exit 1 grep -E '^COMPOSE_FILE=' .env || true docker compose config -q
Якщо COMPOSE_FILE уже визначений у .env, звичайний docker compose може використовувати більше файлів, ніж видно з однієї команди. Я спочатку перевіряю це, а вже потім аналізую окремий сервіс.

Дивимося весь список сервісів, який бачить Compose

Сервіси у фактичній Compose-моделі
cd ~/Projects/laradock || exit 1 docker compose config --services

Це проста, але корисна перевірка. Вона показує не тільки Nginx або PHP-FPM, а всі сервіси, які входять у поточну Compose-модель.

У моєму випадку серед них були окремі PHP-FPM для 8.3, 8.4 і 8.5, відповідні workspace, phpMyAdmin, бази даних, Redis, MinIO, Mailpit, Nginx та інші локальні залежності.

Вже на цьому етапі видно, що Laradock - це не один контейнер Laravel, а граф пов’язаних сервісів.

Перед запуском фіксуємо поточний стан контейнерів

Стан Nginx, workspace і PHP-FPM перед зміною
cd ~/Projects/laradock || exit 1 docker compose ps -a \  nginx \  workspace \  workspace-84 \  workspace-85 \  php-fpm \  php-fpm-84 \  php-fpm-85

У моєму Laradock ім’я workspace відповідало гілці PHP 8.3, а workspace-84 і workspace-85 - PHP 8.4 і 8.5.

Це важлива деталь: назва сервісу не завжди містить повну версію. Якщо дивитися лише на назву контейнера, легко неправильно оцінити, який саме PHP runtime зачепить команда.

Перевіряємо залежності сервісів у вже злитій конфігурації

Сам список сервісів ще не пояснює, чому команда для Nginx зачепила workspace. Для цього потрібно подивитися depends_on після злиття всіх Compose-файлів.

Я не публікую повний docker compose config, тому що він може містити розгорнуті значення змінних середовища. Замість цього можна витягнути тільки назви сервісів та їхні залежності.

Виводимо тільки залежності сервісів
cd ~/Projects/laradock || exit 1 docker compose config --format json |python3 -c 'import jsonimport sys data = json.load(sys.stdin) for name, service in data.get("services", {}).items():    dependencies = service.get("depends_on", {})     if not dependencies:        continue     if isinstance(dependencies, dict):
Якщо встановлена версія Docker Compose не підтримує --format json, залежності можна переглянути через docker compose config локально, але повний вивід не варто копіювати у публічні матеріали без перевірки.

Що сталося в моєму випадку

Після того як захищена чотирифайлова Compose-конфігурація стала конфігурацією за замовчуванням, я звільнила порт 80 і виконала перевірку конфігурації, а потім спробувала запустити Nginx:

Команда, яка виявила приховану для мене залежність
cd ~/Projects/laradock || exit 1 docker compose config -q &&docker compose start nginx

Замість запуску тільки Nginx Compose також почав піднімати workspace-84 і workspace-85. Спроба запустити workspace для PHP 8.3 завершилася помилкою, тому що порт 5173 на хості вже був зайнятий.

Цей результат був важливим: помилка одного workspace перервала очікуваний сценарій запуску Nginx, хоча сам Nginx не був першопричиною.

Помилка порту одного залежного сервісу може виглядати як проблема всього запуску

Хто вже використовує порт 5173
sudo ss -H -ltnp \  '( sport = :5173 )'

Тут важливо не робити висновок "Laravel не запускається". Конфлікт published port - це окрема проблема Docker-рівня.

Спочатку потрібно визначити, хто слухає порт, чи потрібен цей процес, і чи взагалі потрібен workspace 8.3 у поточній роботі. Не варто автоматично завершувати невідомий процес лише для того, щоб Compose пройшов далі.

docker compose up -d може торкнутися ще більшої частини середовища

Пізніше під час відновлення локального середовища звичайний docker compose up -d запустив бази даних, Redis, PostgreSQL, MinIO, Mailpit, phpMyAdmin та workspace 8.4/8.5, а потім зупинився на тому самому конфлікті порту workspace 8.3.

Це хороший приклад того, чому перед up -d потрібно розуміти всю Compose-модель. Така команда призначена не для "одного сайту", а для приведення набору сервісів проєкту до описаного стану.

Коли потрібно запустити рівно один уже створений контейнер

У моєму випадку, коли потрібно було повернути лише вже створений Nginx і не зачіпати workspace, безпечнішим точковим кроком став прямий запуск існуючого контейнера Docker.

Точковий запуск уже створеного Nginx-контейнера
docker start laradock-nginx-1 docker exec \  laradock-nginx-1 \  nginx -t
Прямий docker start не замінює Compose як основний спосіб керування середовищем. Це точковий recovery-крок, коли контейнер уже створений і потрібно уникнути запуску залежностей під час діагностики.

Після невдалого запуску перевіряємо фактичний стан, а не припускаємо

Що реально запустилося після частково невдалої команди
cd ~/Projects/laradock || exit 1 docker compose ps -a echoecho "===== RUNNING =====" docker ps \  --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'

Якщо Compose завершився помилкою на одному сервісі, це не означає, що всі попередні дії були відкочені. Частина контейнерів уже може бути запущена.

Саме тому після помилки я не повторюю команду автоматично. Спочатку дивлюся фактичний стан контейнерів і лише потім вирішую наступний крок.

Мій короткий порядок діагностики

Коли Compose запускає більше, ніж я очікувала, я перевіряю це в такому порядку:

  1. дивлюся COMPOSE_FILE і переконуюся, які файли Compose використовуються;
  2. виконую docker compose config -q;
  3. переглядаю docker compose config --services;
  4. перевіряю depends_on у злитій конфігурації;
  5. фіксую стан потрібних workspace, PHP-FPM і Nginx через docker compose ps -a;
  6. якщо є конфлікт порту, перевіряю власника порту через ss;
  7. після будь-якої часткової помилки знову перевіряю фактичний стан контейнерів і не запускаю ту саму команду навмання.

Команда називає сервіс, але реальну область її впливу визначає вся Compose-модель і залежності між сервісами.

Підсумок

Мій випадок із Laradock показав просту річ: перед запуском Compose недостатньо дивитися лише на назву команди. Потрібно знати, які Compose-файли злиті, які залежності має сервіс і які контейнери вже існують.

docker compose start nginx виглядав як точкова дія, але фактична модель пов’язувала його з workspace. Один конфлікт порту в PHP 8.3 workspace змінив увесь сценарій запуску.

Тому після складного оновлення я спочатку перевіряю конфігурацію і залежності, а вже потім запускаю сервіси. Це значно безпечніше, ніж дізнаватися про граф залежностей під час помилки.