Запустил MCP-сервер из GitHub за 3 клика — без Dockerfile и VPS
Запускаете MCP-серверы или сайты из GitHub-репозиториев? Unyly деплоит их на свой домен без Dockerfile и VPS — пошаговая архитектура и грабли, которые сломали автора

Вы нашли в каталоге MCP-сервер на 40 строк, а запустить его — это поднять VPS, написать Dockerfile, настроить Nginx и выпустить сертификат. Или было так, пока не появился Unyly: подключаете GitHub-репозиторий, получаете работающий проект на slug.unyly.org, а пуш в ветку пересобирает его автоматически. Работает для MCP-серверов на Node и Python, статики, фронтенда и Next.js. В гайде — архитектура, код генерации Dockerfile и грабли, на которых автор потерял два часа и все живые сайты.
Зачем это нужно: проблема дисбаланса усилий
MCP-серверы (Model Context Protocol) — это микро-сервисы, которые позволяют AI-ассистентам (Claude, Cursor, VS Code Copilot) взаимодействовать с кодовыми базами: читать метаданные, запускать тесты, генерировать код. Большинство из них живут не как npm-пакеты, а как GitHub-репозитории. Чтобы запустить такой сервер, нужно:
- Склонировать репозиторий.
- Установить зависимости (
npm installилиpip install). - Собрать проект, если требуется.
- Запустить сервер и держать его живым.
- Обернуть stdio-процесс в HTTP, иначе к нему не подключатся AI-клиенты (Claude, ChatGPT).
Итог: для сервера на 40 строк человек поднимает VPS, пишет Dockerfile, настраивает Nginx, выпускает сертификат и вешает вебхук на автодеплой. Unyly решает эту проблему: подключаете репозиторий, получаете работающий проект на slug.unyly.org, а пуш в ветку пересобирает его автоматически.
Как это работает: архитектура деплой-платформы
Общая схема деплоя в Unyly — это аккуратная склейка стандартных инструментов:
- GitHub push → вебхук на Next.js API (с HMAC-проверкой).
- Запись в очередь
runner_deploys(SQLite). - Демон-воркер (
unyly-runner-worker) берёт задачу:
git cloneс токеном пользователя.- Генерация Dockerfile под тип проекта.
docker buildиdocker runс лимитами.- Healthcheck (для MCP-серверов — JSON-RPC
initialize).
- Реверс-прокси (
unyly-runner-proxy) на порту:3900роутит трафик по заголовкуHost→ нужный контейнер.
Важная деталь: прокси и воркер — это два отдельных процесса под pm2. Объединять их в один нельзя: если воркер упадёт, он утащит за собой прокси, и все сайты лягут. Автор узнал это на практике, когда потерял пару часов и все живые сайты.
Генерация Dockerfile: пять рецептов для разных типов проектов
Пользователь Dockerfile не пишет. Если он есть в репозитории, Unyly использует его. Если нет — генерирует автоматически. Вот пять рецептов:
1. MCP-сервер на Node.js
Точка входа ищется в порядке: bin → main → index.js из package.json. Ключевой момент: stdio-процесс оборачивается в `supergateway`, который превращает stdin/stdout сервера в streamable HTTP на /mcp.
``dockerfile FROM node:22-slim WORKDIR /app COPY package*.json ./ RUN npm install --omit=dev --no-audit --no-fund || npm install --no-audit --no-fund COPY . . RUN npm install -g supergateway@latest --no-audit --no-fund EXPOSE 3000 CMD ["sh", "-c", "supergateway --stateful --stdio 'node index.js' \ --outputTransport streamableHttp --port 3000 --streamableHttpPath /mcp"] ``
2. MCP-сервер на Python
Базовый образ — python:3.12-slim. Зависимости берутся из requirements.txt или pyproject.toml. supergateway (написан на Node) устанавливается через apt-get install nodejs npm. Точка входа — server.py или main.py.
``dockerfile FROM python:3.12-slim WORKDIR /app RUN apt-get update && apt-get install -y nodejs npm COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN npm install -g supergateway@latest --no-audit --no-fund EXPOSE 3000 CMD ["sh", "-c", "supergateway --stateful --stdio 'python server.py' \ --outputTransport streamableHttp --port 3000 --streamableHttpPath /mcp"] ``
3. Статический сайт
Если в репозитории есть index.html и нет package.json, сайт раздаётся через nginx:alpine.
``dockerfile FROM nginx:alpine COPY . /usr/share/nginx/html EXPOSE 80 ``
4. Фронтенд со сборкой
Запускается npm run build, а выходная папка определяется перебором стандартных имён (dist, build, out, public).
```dockerfile FROM node:22-slim as builder WORKDIR /app COPY package*.json ./ RUN npm install --no-audit --no-fund COPY . . RUN npm run build
FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 ```
5. Next.js
Определяется по зависимости next в package.json. Запускается next start.
```dockerfile FROM node:22-slim as builder WORKDIR /app COPY package*.json ./ RUN npm install --no-audit --no-fund COPY . . RUN npm run build
FROM node:22-slim WORKDIR /app COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json RUN npm install --omit=dev --no-audit --no-fund EXPOSE 3000 CMD ["npx", "next", "start"] ```
Запуск контейнера: жёсткие лимиты и безопасность
Собранный образ запускается с ограничениями: это чужой код на вашем железе, поэтому политика — минимум ресурсов, минимум прав.
``bash docker run -d --name <container> --restart unless-stopped \ --memory 384m --cpus 0.5 --pids-limit 256 \ --cap-drop ALL --security-opt no-new-privileges \ -p 127.0.0.1:<port>:3000 \ <image> ``
- Порт слушается только на
localhost— наружу его выставляет реверс-прокси. --cap-drop ALLубирает все Linux capabilities. Исключение —nginxв сайтах: ему нужныCHOWN,SETUID,SETGIDдля переключения воркеров на unprivileged-пользователя. MCP-контейнеры живут без capabilities.
Healthcheck: проверка, которая реально работает
Для сайтов healthcheck — это HTTP-запрос на / (коды 2xx или 3xx). Для MCP-серверов — это JSON-RPC initialize по протоколу MCP с дедлайном 60 секунд:
``bash curl -s --max-time 5 -X POST http://127.0.0.1:<port>/mcp \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": { "name": "unyly-health", "version": "1.0" } } }' ``
Если в ответе есть "result", сервер действительно говорит на MCP. Если healthcheck не прошёл за минуту, в лог деплоя выводятся последние 40 строк docker logs — чтобы пользователь видел причину, а не просто "failed".
Роутинг по Host: как не подорваться на wildcard-поддомене
Реверс-прокси на порту :3900 смотрит на заголовок Host и выбирает контейнер: slug.unyly.org, кастомный домен или превью пулреквеста. Nginx отдаёт весь wildcard-поддомен в этот прокси, но здесь спрятана мина.
Правильный server_name — это регулярное выражение с негативным просмотром вперёд, которое исключает служебные поддомены:
``nginx server_name ~^(?!(www|app|api|admin|gateway|staging|run|mail|smtp|ftp|deploy)\.)[a-z0-9-]+\.unyly\.org$; ``
Простой *.unyly.org ставить нельзя: он перехватит трафик служебных поддоменов (app.unyly.org, api.unyly.org), и платформа сломается.
Где ломается: грабли, на которых потерял время
Если объединить прокси и воркер в один процесс под pm2, то при падении воркера упадёт и прокси, и все сайты лягут. Решение: два отдельных процесса (unyly-runner-proxy и unyly-runner-worker).
- Прокси и воркер в одном процессе
Простой *.unyly.org перехватывает трафик служебных поддоменов (app.unyly.org, api.unyly.org). Решение: регулярное выражение с негативным просмотром вперёд.
- Wildcard-поддомен в Nginx
В nginx-конфиге для SPA-фолбэка нельзя использовать $uri в try_files, потому что она содержит декодированный путь (с пробелами, %20). Решение: использовать $request_uri (не декодируется).
- Переменная `$uri` в Nginx
Обычный HTTP-запрос на / не гарантирует, что сервер действительно говорит на MCP. Решение: JSON-RPC initialize с дедлайном 60 секунд.
- Healthcheck для MCP-серверов
supergateway написан на Node, поэтому в Python-контейнерах нужно ставить nodejs и npm через apt-get. Без этого supergateway не установится, и сервер не запустится.
- Зависимости в MCP-серверах на Python
Что попробовать дальше
Зарегистрируйтесь на Unyly и подключите GitHub-репозиторий с MCP-сервером или сайтом. Получите работающий проект на slug.unyly.org.
- Подключить свой репозиторий
Включите вебхук на GitHub, чтобы пуш в ветку автоматически пересобирал проект.
- Настроить автодеплой
Настройте CNAME-запись на unyly.org и подключите свой домен к проекту.
- Добавить кастомный домен
Создайте пулреквест в репозитории — Unyly автоматически развернёт превью на отдельном поддомене.
- Попробовать превью пулреквестов
Архитектура Unyly открыта: GitHub-репозиторий. Можно адаптировать её для своих нужд.
- Изучить исходники
Источники
Читайте также
Комментарии
Пока никто не написал. Будьте первым.

