Docker Compose: PHP, MySQL, Nginx и phpMyAdmin - локальный стенд и связка с HTTP из 1С

06.10.26

Разработка - DevOps и автоматизация разработки

Нужен локальный PHP/MySQL-стенд для проверки HTTP-интеграций с 1С без Denwer и без установки LAMP в Windows? Соберём стек через Docker Compose: один compose-файл, изолированные сервисы, данные MySQL в named volume и вызов тестового endpoint из встроенного языка 1С.

Docker и Docker Compose позволяют поднять на рабочей машине согласованный набор PHP, MySQL, Nginx и phpMyAdmin без «монолитной» локальной сборки вроде Denwer и без смешивания версий с системными службами Windows. Один сомросе-файл описывает сервисы, порты, сеть и тома; исходники PHP и конфиги Nginx лежат в каталоге проекта и правятся как обычные файлы. К концу настройки получится воспроизводимый изолированный веб-стек: сайт на порту 80, phpMyAdmin на 8000, база с отдельным пользователем приложения и возможность вызвать тот же НТТР-сервис из 1С на том же ПК через localhost.

 

Зачем Docker Compose вместо «всё-ин-оне» на Windows

Раньше я держал PHP, MySQL и phpMyAdmin в одной «коробке» под Windows. На старте это удобно, но версии PHP и MySQL жёстко привязаны к пакету, каталоги разъезжаются с рабочими проектами, а второй параллельный стенд под другую ветку интеграции превращается в отдельную возню с портами и службами.

Контейнеры решают другую задачу: каждый компонент живёт в своём образе с зафиксированной версией, не регистрируется в системе как служба MySQL или Apache и переносится на другой ПК по схеме «склонировал репозиторий - compose up». Docker Compose сводит описание стека в один YAML: зависимости сервисов, порты на хосте, монтирование каталогов с машины разработчика, место хранения данных БД между пересборками образа php.

Для разработчика или администратора 1С это не замена клиент-серверной базы платформы и не публикация НТТР-сервисов самой 1С на IIS. Это отдельный PHP и MySQL на хосте, к которым удобно ходить из конфигурации через НТТРСоединение, когда нужно проверить обмен JSON или XML с внешним API, не разворачивая полноценный веб-сервер в прод-контуре.

Главное, что я держу в голове при локальной работе: изоляция и переносимость. Код правите в src/public, перезапускаете только нужный сервис; данные MySQL не пропадают после docker compose build, если том подключён как named volume, а не как временный каталог внутри контейнера.

 

Подготовка: Docker Desktop и структура проекта

На Windows ставлю Docker Desktop с официального сайта Docker. После установки проверяю, что в терминале отвечают docker version и docker compose version. Актуальный синтаксис - docker compose (пробел); старый бинарь docker-compose ещё встречается в статьях и скриптах, смысл команд тот же. Эталонную раскладку каталогов беру из публичного репозитория komissarovev/docker_php на GitHub: клонирую себе, открываю корень проекта в терминале.

До первой сборки смотрю карту каталогов: где лежит public, откуда Nginx подхватывает conf.d и в какой папке собирается php-fpm.

Каталоги src, nginx и php относительно сомросе-файла

Путаница между путями на хосте и путями внутри контейнера даёт «белый экран» и 404 на .php; иллюстрация выше как раз про привязку src, nginx и php к одному сомросе-файлу.

Сомросе-файл задаёт общую сеть tools_php-network (имя может отличаться у вашей копии): внутри неё сервисы видят друг друга по ДНС-имени сервиса - mysql, php, nginx, а не по 127.0.0.1. На хосте доступны только опубликованные порты, обычно 80 для Nginx и 8000 для phpMyAdmin.

Пароли и логины для учебного стенда выношу в .env и подключаю через env_file в compose, чтобы не коммитить секреты в git. В репозиториях для статей иногда оставляют явный MYSQL_ROOT_PASSWORD в YAML - для своей машины привычнее сразу работать через .env.

 

Запуск стека и остановка без потери данных

Первый проход: из корня проекта docker compose build собирает образ сервиса php по Dockerfile, остальные сервисы тянут готовые образы mysql, nginx, phpmyadmin. Затем docker compose up поднимает контейнеры и пишет логи в текущую консоль. Если менял Dockerfile или набор расширений, удобнее сразу docker compose up --build.

Для фоновой работы добавляю флаг -d: docker compose up --build -d. Консоль освобождается, контейнеры остаются запущенными. Запущенный без -d стек останавливаю Ctrl+C; это не всегда равно полному удалению контейнеров. Чтобы остановить и убрать контейнеры проекта, использую docker compose down. Named volume с данными MySQL при обычном down не удаляется, пока явно не указать -v.

После успешного up открываю в браузере localhost:80 - должна открыться страница из src/public (часто index.php). phpMyAdmin слушает localhost:8000, потому что 80-й порт на хосте уже отдан Nginx.

Если порт 80 занят Skype, IIS или другим стендом, compose упадёт с ошибкой bind. Либо освобождаю порт, либо меняю левую часть маппинга ports у nginx, например 8080:80, и тогда URL сайта и вызовы из 1С строю с тем же портом.

Пересборка образа php не должна пересоздавать базу: данные лежат в named volume mysql_php, подключённом к сервису mysql. Команда build меняет слои образа приложения, том живёт отдельно. Я разделяю «сломался РНР-код или Dockerfile» (build/up php) и «сломалась схема БД» (миграции, дамп, phpMyAdmin).

 

MySQL: том, root и пользователь приложения

В compose у mysql задают образ с фиксированным тегом, например mysql:8.0.29, переменную MYSQL_ROOT_PASSWORD и монтирование тома mysql_php в каталог данных MySQL внутри контейнера (в эталонном проекте путь в volume описан так, чтобы данные переживали rebuild). Root нужен для администрирования, приложению на PHP root не даю.

Базу и пользователя создаю через docker exec в контейнер mysql, не обязательно через GUI. Имя контейнера смотрю в container_name в compose или в docker ps: docker exec -it tools_php-mysql sh (имя замените на своё), внутри контейнера mysql -uroot -p и пароль из compose или .env.

CREATE DATABASE `demo_app`;
SHOW DATABASES;
CREATE USER 'app_user'@'%' IDENTIFIED BY 'задайте_пароль';
GRANT ALL PRIVILEGES ON `demo_app`.* TO 'app_user'@'%';
FLUSH PRIVILEGES;

Хост '%' в MySQL означает «подключение с любого хоста в сети сервера БД». В Docker у каждого контейнера свой IP; php подключается к хосту mysql по имени сервиса, не с localhost контейнера php. Если создать пользователя только для 'localhost' внутри контейнера mysql, php из соседнего контейнера часто не проходит аутентификацию.

Удаление пользователя, если переделываю стенд: сначала REVOKE, затем DROP USER. Пароль root храню только для админских операций и входа в phpMyAdmin при отладке; в переменных сервиса php указываю app_user и demo_app.

MySQL 8 по умолчанию использует плагин caching_sha2_password. Расширения pdo_mysql и mysqli в php-fpm 8.2 и 8.3 обычно работают с ним из коробки. Если клиент ругается на метод аутентификации, проверяю версию образа mysql и при необходимости создаю пользователя заново или смотрю default_authentication_plugin в конфиге образа - для учебного стенда чаще проще пересоздать пользователя с актуальным клиентом PHP, чем откатывать mysql на 5.7.

Логический бэкап для переноса на другой ПК: через phpMyAdmin на порту 8000 (экспорт demo_app) или mysqldump из exec. Физически данные named volume лежат в хранилище Docker; для переноса между машинами мне удобнее логический дамп, чем копирование volume без опыта.

 

phpMyAdmin и Nginx: порты, сеть, PHP-FPM

Сервис phpmyadmin в compose тянет образ phpmyadmin; тег latest для прод-подобного стенда я заменяю на конкретный, чтобы обновление образа не меняло поведение неожиданно. Переменная PMA_ARBITRARY=1 разрешает вход не только на преднастроенный хост: в форме логина указываю сервер mysql, пользователя app_user или root, пароль из .env.

UPLOAD_LIMIT, MEMORY_LIMIT и MAX_EXECUTION_TIME поднимают потолок для импорта дампов через веб-интерфейс. Без них большой SQL из бэкапа обрывается на дефолтных лимитах PHP внутри контейнера phpMyAdmin. depends_on: mysql задаёт порядок старта; готовность mysql к подключениям всё равно стоит проверить повторным входом, если контейнер mysql только что поднялся.

Маппинг 8000:80 пробрасывает веб-интерфейс phpMyAdmin на хост: внутри контейнера Apache слушает 80, снаружи мы не конфликтуем с Nginx. depends_on связывает phpMyAdmin с mysql в одной user-defined сети compose.

Nginx публикует 80:80, монтирует ./src в /var/www и ./nginx/conf.d в /etc/nginx/conf.d. Сверяю сервер-блок с тем, что реально лежит в nginx/conf.d:

server {
    listen 80;
    root /var/www/public;
    index index.php;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass php:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

Строка fastcgi_pass php:9000 связывает Nginx с сервисом php по DNS compose; SCRIPT_FILENAME должен указывать на файл внутри смонтированного /var/www.

location / с try_files отправляет «красивые» URL на index.php при необходимости. Если PHP отвечает 502, смотрю, запущен ли контейнер php, совпадает ли имя в fastcgi_pass с именем сервиса в compose и нет ли опечатки в SCRIPT_FILENAME.

Критично не путать два уровня имён: на хосте 127.0.0.1 или localhost и порты 80 и 8000; внутри сети compose - mysql, php:9000. Подмена MYSQL_HOST на 127.0.0.1 в environment сервиса php ломает подключение: внутри контейнера php localhost - это сам php, а не mysql.

 

Сервис PHP: Dockerfile, расширения, переменные окружения

Сервис php в compose использует build:./php и Dockerfile на базе php:8.2-fpm или php:8.3-fpm. Базовый тег в Dockerfile я обновляю, сохраняя тот же набор расширений: docker-php-ext-install pdo mysqli pdo_mysql, при необходимости ставлю Composer для зависимостей проекта. Образ собирается на build; после правок Dockerfile нужен compose build php.

working_dir /var/www и volume ./src:/var/www дают тот же код, что видит Nginx. В environment сервиса php передаю MYSQL_HOST=mysql, MYSQL_DATABASE=demo_app, MYSQL_USER=app_user, MYSQL_PASSWORD - значения должны совпасть с GRANT в MySQL и с тем, что вводите в phpMyAdmin.

В index.php или общем include читаю их через $_ENV и подключаюсь к TCP mysql:3306, без сокета localhost:

$link = mysqli_connect(
    $_ENV['MYSQL_HOST'],
    $_ENV['MYSQL_USER'],
    $_ENV['MYSQL_PASSWORD'],
    $_ENV['MYSQL_DATABASE']
);

Для нового кода тот же набор параметров удобно передать в PDO с DSN вида mysql:host=…;dbname=…; для проверки стенда достаточно вывести результат простого SELECT на странице порта 80.

Проверка связки: страница на порту 80 показывает результат запроса от app_user к demo_app. Ошибка Access denied - неверный пароль или пользователь не создан с '%'. Unknown MySQL сервер host - опечатка в MYSQL_HOST или контейнер mysql не в той сети. No such file or directory при mysqli - часто путаница с сокетом; для Docker нужен ТСР-хост mysql:3306, не localhost socket.

Образы mysql и nginx фиксирую тегами (mysql:8.0.x, nginx:stable-alpine), phpMyAdmin - выбираю тег осознанно, а не тяну latest без контроля. PHP через свой Dockerfile и так пересобирается при изменениях; инфраструктурные образы на новой машине не должны «уплывать» после каждого compose pull.

 

Вызов из 1С, актуальные теги и очистка Docker

На том же ПК, где крутится Docker Desktop, платформа 1С ходит на опубликованный порт хоста, а не к имени сервиса mysql. Для сайта при стандартном маппинге это HTTP к localhost на порт 80; для phpMyAdmin - порт 8000.

На рисунке ниже цепочка HTTP: запрос с сервера 1С на localhost:80, ответ JSON от PHP за Nginx. Это проверка внешнего API, не обмен с базой 1С.

Запрос из 1С на localhost:80 и ответ JSON от PHP

SSL для localhost обычно не нужен; если включить HTTPS в Nginx без доверенного сертификата, НТТРСоединение из 1С может упасть на проверке сертификата. Заголовки Content-Type и кодировка тела должны совпадать с тем, как вы разбираете ответ в 1С; для JSON удобно UTF-8 без BOM.

В src/public добавляю простой endpoint, например api/ping.php, который возвращает JSON с полем status. Так отделяю «дошёл ли HTTP» от «отработала ли логика БД».

Ниже фрагмент для общей формы или внешней обработки: GET к ping.php, разбор JSON через ЧтениеJSON. URL тестового API держу в константе или реквизите настройки интеграции, а не в десяти модулях подряд.

&НаСервере
Функция ПроверитьЛокальныйPhpСтенд(АдресСервера = "localhost", Порт = 80) Экспорт
	
	Путь = "/api/ping.php";
	Соединение = Новый HTTPСоединение(АдресСервера, Порт,,,, 30);
	Запрос = Новый HTTPЗапрос(Путь);
	Запрос.Заголовки.Вставить("Accept", "application/json; charset=utf-8");
	
	Ответ = Соединение.Получить(Запрос);
	
	Если Ответ.КодСостояния <> 200 Тогда
		ВызватьИсключение "HTTP " + Формат(Ответ.КодСостояния, "ЧГ=0")
			+ ": " + Ответ.ПолучитьТелоКакСтроку("UTF-8");
	КонецЕсли;
	
	Тело = Ответ.ПолучитьТелоКакСтроку("UTF-8");
	Чтение = Новый ЧтениеJSON;
	Чтение.УстановитьСтроку(Тело);
	Данные = ПрочитатьJSON(Чтение);
	Чтение.Закрыть();
	
	Если ТипЗнч(Данные) <> Тип("Структура") Тогда
		ВызватьИсключение "Ожидалась структура JSON в ответе PHP";
	КонецЕсли;
	
	Если Не Данные.Свойство("status") Тогда
		ВызватьИсключение "В ответе нет поля status";
	КонецЕсли;
	
	Возврат Данные.status = "ok";
	
КонецФункции

НТТРСоединение задаёт хост, порт и таймаут в секундах; для тяжёлых сценариев таймаут увеличиваю, иначе при отладке с точкой останова в PHP получу обрыв. НТТРЗапрос несёт путь без схемы и хоста. Код 404 часто значит неверный путь или root Nginx смотрит не туда; connection refused - compose не поднят или на хосте другой порт. Блокировка firewall на localhost встречается реже, но корпоративный агент иногда режет обращения к 127.0.0.1 - проверяю тем же URL из браузера на той же машине.

Для POST с JSON телом использую НТТРЗапрос с телом строкой и заголовком Content-Type: application/json; charset=utf-8; метод задаётся при вызове ОтправитьДляОбработки или аналога - сверяюсь со справкой платформы по НТТРМетод, если переношу пример с GET на POST.

Замкнутый сценарий от клонирования репозитория до ответа функции на сервере 1С помогает не пропустить шаг после очередного compose down.

От clone до проверки функции на сервере 1С

compose up, demo_app и app_user в mysql, согласованные MYSQL_* у php, ping.php в браузере, затем вызов проверочной функции на сервере 1С - порядок, по которому я сам прохожу стенд после чистой машины.

Когда стенд на другой машине не нужен, docker system prune удаляет неиспользуемые образы, остановленные контейнеры и сети без привязки к томам. На рабочей машине с несколькими проектами prune не жму вслепую: можно снести образы, которые потом долго качаются заново. Для одного учебного compose после экспериментов prune освобождает место; named volume mysql_php prune по умолчанию не трогает, но docker volume prune уже опасен для данных, если volume не примонтирован.

Перед сдачей задачи интеграции проверяю: docker compose ps показывает все сервисы Up; в phpMyAdmin под app_user видна demo_app; сайт на порту 80 и ping.php отдают JSON; функция на сервере 1С возвращает Истина. Если что-то из цепочки падает, иду по порядку сети compose, а не меняю сразу код обмена в конфигурации.

Вступайте в нашу телеграмм-группу Инфостарт

Docker HTTP-сервисы Интеграция MySQL PHP Nginx Разработка Windows Docker Compose phpMyAdmin 1С HTTPСоединение локальный стенд интеграция HTTP php-fpm named volume

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Инструментарий разработчика Нейросети DevOps и автоматизация разработки Тестирование QA Разработчик 1С 8.3 Бесплатно (free)

Открытый набор правил и скриптов на GitHub для ИИ-агента (агента искусственного интеллекта: Claude Code, Codex и других). Агент сам снимает опись машины, предлагает путь проверки кода: плагин Unica или BSL Language Server со справкой платформы. С вашего «да» ставит недостающее, затем выгружает внешнюю обработку, заводит git и доводит её до зелёного теста YAxUnit. Оба пути проверены с нуля свежим агентом на копии демобазы «Управление торговлей» 11.5, платформа 8.3.27. Новая часть: для задач по данным базы агент выгружает конфигурацию в XML, строит карту объектов и пишет запросы только по именам, найденным в выгрузке. Внутри гайд «Unica на Windows» с известными ошибками и проверенными обходами.

10.11.2026    406    Iliaelagin    0    

1

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Системный администратор Разработчик 1С 8.3 1С 8.5 Бесплатно (free)

В прошлой статье у ERP оставалось 89 отказов загрузки, у ЗУП — 19. Теперь обе конфигурации совпали с эталонами, а собранные файлы прошли круг через платформу. Развертка выделена в открытый репозиторий cf-unwrapper: библиотека на Rust и консольная утилита. Показываю результаты, объясняю границы «100%» и приглашаю к проверкам и развитию проекта.

02.11.2026    317    oyti    1    

1

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Разработчик 1С:Предприятие 8 Россия Бесплатно (free)

Практическое руководство для команд, которые хотят уйти с хранилища конфигурации на git, не переходя на новую среду разработки. Подойдёт разработчикам и тимлидам, которые настраивают командную разработку на типовых и собственных конфигурациях.

02.11.2026    633    vanya_gonchar    2    

6

Файловый обмен (TXT, XML, DBF), FTP DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Системный администратор Разработчик 1С 8.5 Бесплатно (free)

В прошлой статье я выгрузил Бухгалтерию в XML байт в байт и спросил, на чем проверить дальше. Вы ответили: ERP, старые релизы, зарплата. Прогнал пять конфигураций, от клиента ЭДО до ERP на 120 тысяч файлов. Рассказываю, что сломалось после Бухгалтерии, почему эталону платформы нельзя верить на слово, как выгрузить одну конфигурацию в формат трех версий платформы - и почему cf-конвертер становится отдельным продуктом, Разверткой.

02.11.2026    422    oyti    2    

3

Инструментарий разработчика DevOps и автоматизация разработки Разработчик 1С 8.3 Абонемент ($m)

Как отличить срочное исправление от обычного вечернего коммита? Проверила расчёт показателей поставок по отчёту хранилища 1С и истории двух открытых проектов. На стенде счётчики совпали со сценарием, а в выборке открытого проекта правило «те же объекты» ошиблось во всех шести случаях. Показываю границы такого замера и отличие от метрик DORA. Прилагаются инструмент и материалы опыта.

1 стартмани

29.10.2026    378    Ninel_S    0    

0

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Файловый обмен (TXT, XML, DBF), FTP Системный администратор Разработчик 1С 8.5 Бесплатно (free)

Выгрузка типовой Бухгалтерии в XML через базу занимает больше четырёх минут. Я написал движок, который читает файл конфигурации напрямую и выгружает его за 35 секунд, совпадая с выгрузкой платформы байт в байт во всех 90 385 файлах. Загрузка обратно тоже работает: круг через платформу совпадает полностью. Рассказываю, как ускоряли, как проверяем и что при загрузке XML теряет сама платформа. А в конце спрашиваю, нужно ли это кому-то, кроме меня, и на какой конфигурации проверить следующей.

27.10.2026    984    oyti    20    

16

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Разработчик 1С 8.3 Абонемент ($m)

Хук pre-commit проверяет полный снимок индекса Git в отдельной базе 1С. На «Нано-УТ» и платформе 8.3.27 повторила 12 попыток коммита: восемь прошли, четыре остановлены. Чистый коммит занял 31,8 с. Вызов несуществующего метода общего модуля хук останавливает только с ключом -ExtendedModulesCheck. В архиве версия 2, тесты, инструкция и журналы.

1 стартмани

26.10.2026    403    Ninel_S    2    

-1

Нейросети DevOps и автоматизация разработки Разработчик 1С 8.3 Абонемент ($m)

Я дала агенту Claude Code задачу словами: добавить в учебную конфигурацию документ «Внутреннее потребление товаров», который списывает товар по средней стоимости и не даёт уйти в минус. Три запуска с нуля, каждый принят проверкой на платформе 1С: 1 мин 49 с, 2 мин 33 с и 2 мин 56 с. Показываю, что агент поменял, где ошибался и почему на типовой УТ так быстро не будет.

1 стартмани

26.10.2026    722    Ninel_S    0    

-1
Для отправки сообщения требуется регистрация/авторизация