# Запустил MCP-сервер из GitHub за 3 клика — без Dockerfile и VPS
Каноническая страница: https://plainews.ru/posts/deploy-mcp-server-bez-vps
Опубликовано: 2026-08-15T07:01:08.356Z

Запускаете 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-репозитории. Чтобы запустить такой сервер, нужно:

1. Склонировать репозиторий.
2. Установить зависимости (npm install или pip install).
3. Собрать проект, если требуется.
4. Запустить сервер и держать его живым.
5. Обернуть stdio-процесс в HTTP, иначе к нему не подключатся AI-клиенты (Claude, ChatGPT).

Итог: для сервера на 40 строк человек поднимает VPS, пишет Dockerfile, настраивает Nginx, выпускает сертификат и вешает вебхук на автодеплой. Unyly решает эту проблему: подключаете репозиторий, получаете работающий проект на slug.unyly.org, а пуш в ветку пересобирает его автоматически.

## Как это работает: архитектура деплой-платформы

Общая схема деплоя в Unyly — это аккуратная склейка стандартных инструментов:

1. **GitHub push** → вебхук на Next.js API (с HMAC-проверкой).
2. Запись в очередь runner_deploys (SQLite).
3. Демон-воркер (unyly-runner-worker) берёт задачу:

- git clone с токеном пользователя.
- Генерация Dockerfile под тип проекта.
- docker build и docker run с лимитами.
- Healthcheck (для MCP-серверов — JSON-RPC initialize).

1. Реверс-прокси (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).

1. **Прокси и воркер в одном процессе**

Простой *.unyly.org перехватывает трафик служебных поддоменов (app.unyly.org, api.unyly.org). Решение: регулярное выражение с негативным просмотром вперёд.

1. **Wildcard-поддомен в Nginx**

В nginx-конфиге для SPA-фолбэка нельзя использовать $uri в try_files, потому что она содержит декодированный путь (с пробелами, %20). Решение: использовать $request_uri (не декодируется).

1. **Переменная `$uri` в Nginx**

Обычный HTTP-запрос на / не гарантирует, что сервер действительно говорит на MCP. Решение: JSON-RPC initialize с дедлайном 60 секунд.

1. **Healthcheck для MCP-серверов**

supergateway написан на Node, поэтому в Python-контейнерах нужно ставить nodejs и npm через apt-get. Без этого supergateway не установится, и сервер не запустится.

1. **Зависимости в MCP-серверах на Python**

## Что попробовать дальше

Зарегистрируйтесь на Unyly и подключите GitHub-репозиторий с MCP-сервером или сайтом. Получите работающий проект на slug.unyly.org.

1. **Подключить свой репозиторий**

Включите вебхук на GitHub, чтобы пуш в ветку автоматически пересобирал проект.

1. **Настроить автодеплой**

Настройте CNAME-запись на unyly.org и подключите свой домен к проекту.

1. **Добавить кастомный домен**

Создайте пулреквест в репозитории — Unyly автоматически развернёт превью на отдельном поддомене.

1. **Попробовать превью пулреквестов**

Архитектура Unyly открыта: GitHub-репозиторий. Можно адаптировать её для своих нужд.

1. **Изучить исходники**

## Источники

- Habr AI: Деплой-платформа для MCP-серверов и сайтов, встроенная в каталог
