ComfyUI в n8n: запуск workflow из автоматизации
Готовый рецепт интеграции ComfyUI с n8n: HTTP Request nodes для запуска воркфлоу, polling статуса, выгрузка результата, обработка CUDA OOM и таймаутов. Без своего GPU.
Обновлено: 2026-05-19
TL;DR
n8n хорошо умеет HTTP, plumbing и оркестрацию. ComfyUI хорошо умеет GPU и сложные графы Stable Diffusion. Связать их через HTTP Request — тривиально, если ComfyUI живёт там, где есть GPU и куда n8n может постучаться по сети. Самые частые грабли — это не интеграция, а инфраструктура: куда деть ComfyUI, как переживать долгие задачи и что делать при CUDA out of memory. Этот гайд показывает рабочую конфигурацию end-to-end: запрос → polling → результат → ошибки.
Где живёт ComfyUI в этой схеме
Три рабочих варианта:
- Self-hosted ComfyUI: своя машина с RTX 4090/3090 или арендованный bare-metal GPU. Хорошо для R&D и низких объёмов, болезненно для продакшена (один воркер, нет HA, ручной мониторинг VRAM).
- Managed ComfyUI API (gpupool, ComfyICU, fal.ai workflows и т.д.): вы отправляете workflow JSON в облако, оно крутит ComfyUI с нужными моделями/нодами и возвращает результат. n8n общается с одним HTTP-эндпоинтом, не задумываясь о GPU.
- k8s-deployment: свой ComfyUI в кластере с GPU-нодами. Подходит, если уже есть инфраструктурная команда и нужны кастомные custom_nodes, которые не пускают в managed-сервисы.
Дальнейший гайд работает для всех трёх вариантов — отличается только базовый URL и аутентификация.
Архитектура запроса из n8n
Минимальный happy-path выглядит так:
[Trigger] → [Set: prompt+params] → [HTTP Request: POST /prompt]
→ [Wait 5s] → [HTTP Request: GET /history/{id}]
→ [If: status == success] → [HTTP Request: GET image] → [Save/Send]
↘ [If failed] → [Error handler]
В классическом ComfyUI API (/prompt, /history, /view) нет встроенного webhook — приходится поллить. Managed-API обычно поддерживает оба варианта: polling и webhook-callback на ваш n8n trigger.
Шаг 1: подготовить workflow JSON
ComfyUI хранит workflow в двух форматах: «UI workflow» (с координатами нод для визуализации) и «API workflow» (только функциональная часть). Для запуска через API нужен второй.
Чтобы экспортировать:
- В ComfyUI UI: Settings (шестерёнка) → Enable Dev mode Options.
- После этого в меню появится
Save (API Format).
- Сохраните JSON — это и есть нагрузка для
POST /prompt.
Пример workflow JSON для SDXL текст→изображение (сильно сокращённый):
{
"3": {
"class_type": "KSampler",
"inputs": {
"seed": 0,
"steps": 30,
"cfg": 7.5,
"sampler_name": "dpmpp_2m",
"scheduler": "karras",
"denoise": 1.0,
"model": ["4", 0],
"positive": ["6", 0],
"negative": ["7", 0],
"latent_image": ["5", 0]
}
},
"4": { "class_type": "CheckpointLoaderSimple", "inputs": { "ckpt_name": "sd_xl_base_1.0.safetensors" } },
"5": { "class_type": "EmptyLatentImage", "inputs": { "width": 1024, "height": 1024, "batch_size": 1 } },
"6": { "class_type": "CLIPTextEncode", "inputs": { "text": "photo of a red fox", "clip": ["4", 1] } },
"7": { "class_type": "CLIPTextEncode", "inputs": { "text": "blurry, low quality", "clip": ["4", 1] } },
"8": { "class_type": "VAEDecode", "inputs": { "samples": ["3", 0], "vae": ["4", 2] } },
"9": { "class_type": "SaveImage", "inputs": { "filename_prefix": "n8n", "images": ["8", 0] } }
}
Реальные графы будут больше: LoRA-loaders, ControlNet, IP-Adapter, upscaler — но контракт API остаётся тем же.
Шаг 2: переменные части как Set node
Хардкодить prompt в JSON — путь к хаосу. Заведите Set node прямо перед HTTP Request и положите туда то, что меняется:
prompt = {{ $json.body.prompt }}
negative = "blurry, low quality, watermark"
seed = {{ Math.floor(Math.random() * 1e9) }}
width = 1024
height = 1024
steps = 30
Дальше workflow JSON хранится в Static Data или Git и инжектится через Function node:
// Function node: build_workflow.js
const wf = JSON.parse($getWorkflowStaticData('global').comfyui_sdxl_base);
wf['3'].inputs.seed = $node['Set'].json.seed;
wf['3'].inputs.steps = $node['Set'].json.steps;
wf['5'].inputs.width = $node['Set'].json.width;
wf['5'].inputs.height = $node['Set'].json.height;
wf['6'].inputs.text = $node['Set'].json.prompt;
wf['7'].inputs.text = $node['Set'].json.negative;
return [{ json: { workflow: wf } }];
Шаг 3: POST /prompt из HTTP Request node
В HTTP Request node:
- Method: POST
- URL:
https://your-comfyui-host/prompt (или endpoint managed-API, например https://api.gpupool.ru/v1/comfyui/run)
- Authentication: Header Auth →
Authorization: Bearer <API_TOKEN> (для managed-API)
- Body: JSON
- JSON Body:
{
"prompt": {{ $json.workflow }},
"client_id": "n8n-{{ $workflow.id }}-{{ $execution.id }}"
}
client_id важен: managed-API обычно прокидывает его в идемпотентность и в логи. Для self-hosted ComfyUI идемпотентности нет, но client_id всё равно нужен — по нему фильтруется /history.
В ответе вы получите {"prompt_id": "abc-123", "number": 42}. Сохраняйте prompt_id — по нему дальше поллим.
Шаг 4: polling статуса
ComfyUI без webhook → нужен loop. В n8n идиоматичная схема:
HTTP POST /prompt → Set: prompt_id
→ SplitInBatches (size=1, fake loop ограниченный счётчиком)
└── Wait 5s
└── HTTP GET /history/{prompt_id}
└── If: response[prompt_id] существует и outputs не пуст?
├── Yes: extract image URL, break
└── No: increment attempt, продолжить loop
Реальнее — использовать Wait node + Code node со счётчиком:
// Code node: poll_or_continue.js
const attempt = ($json.attempt || 0) + 1;
const maxAttempts = 60; // 60 * 5s = 5 минут
if (attempt > maxAttempts) {
throw new Error(`Timeout: ComfyUI task ${$json.prompt_id} did not finish in ${maxAttempts * 5}s`);
}
return [{ json: { ...$json, attempt } }];
Для managed-API, который умеет webhook, polling вообще не нужен — отдельный n8n webhook-trigger принимает callback с готовым URL результата.
Шаг 5: забрать изображение
После того как /history/{prompt_id} вернул outputs, в нём будет что-то вроде:
{
"abc-123": {
"outputs": {
"9": {
"images": [
{ "filename": "n8n_00001_.png", "subfolder": "", "type": "output" }
]
}
}
}
}
Скачать файл — GET на /view:
GET /view?filename=n8n_00001_.png&subfolder=&type=output
В HTTP Request включите Response → Binary File — тогда n8n положит PNG в binary поле item. Дальше его можно отправить в Telegram (sendPhoto), S3 (Upload Object), Google Drive, или прокинуть как ответ webhook-триггера.
Managed-API обычно сразу возвращают публичный CDN URL результата — скачивать через /view не нужно.
Обработка ошибок
Главные сценарии отказа:
| Симптом |
Причина |
Что делать |
HTTP 400 на /prompt |
Неверный JSON, отсутствуют ноды |
Валидировать workflow JSON через test-run в ComfyUI UI |
/history пуст после maxAttempts |
GPU занят, очередь длинная |
Увеличить timeout, добавить алерт, рассмотреть отдельную priority-очередь |
CUDA out of memory в логах |
Модель/разрешение не влезают в VRAM |
Self-hosted: --lowvram, tiled VAE, batch_size=1. Managed: уменьшить разрешение или сменить тариф GPU |
| HTTP 429 / rate limit |
Managed-API ограничил concurrency |
SplitInBatches с batchSize=1, Wait между batch |
| Workflow выполнился, но image пустой/чёрный |
Неподходящая LoRA, конфликт VAE |
Логи ComfyUI (managed: отдельный endpoint /v1/tasks/{id}/logs), визуальная проверка на тестовом прогоне |
503 Service Unavailable |
Сервер ComfyUI рестартует |
Retry с экспоненциальным backoff (n8n: HTTP Request → Options → Retry On Fail) |
Поставьте отдельную ветку Error workflow в n8n (Settings → Error Workflow): туда полетят все необработанные исключения, и вы их прицепите к Telegram/Slack-алерту.
Когда не стоит использовать n8n как orchestrator ComfyUI
n8n удобен для бизнес-автоматизаций (CRM → генерация → CRM-обратно), но плох как очередь высоконагруженных GPU-задач. Если у вас:
1000 задач/час,
- сложная маршрутизация по приоритетам,
- многоступенчатые пайплайны (генерация → upscale → инпейнтинг → review),
— лучше вынести оркестрацию в Temporal / Hatchet / Redis Streams + dedicated worker. n8n оставить для пользовательского сценария «триггер → одна задача → результат».
Полный пример n8n workflow
Сжатая схема, которую можно собрать за 30 минут (без UI-выгрузки):
- Webhook Trigger —
POST /generate с body { prompt, aspect_ratio }.
- Function: build_workflow — берёт ComfyUI API JSON из staticData, подставляет prompt и размеры.
- HTTP Request: POST /v1/comfyui/run — managed-API.
- Set: prompt_id, attempt=0.
- Loop start (через себя через If):
- Wait 5s
- HTTP Request: GET /v1/tasks/{prompt_id}
- If: status == "success" → выходим из loop.
- If: status == "failed" → бросаем error, отправляем алерт.
- If: attempt >= 60 → timeout error.
- Code: attempt++ → обратно в loop.
- HTTP Request: GET /v1/tasks/{prompt_id}/result → binary PNG.
- AWS S3: Upload или Telegram: sendPhoto.
- Respond to Webhook —
{ status: "ok", url }.
Чек-лист перед прод-релизом
Частые вопросы
Можно ли использовать ComfyUI в n8n без своего GPU?
Да. ComfyUI выносится на managed-API (например, gpupool), а n8n общается с ним через HTTP Request node. Локально не нужны ни CUDA, ни видеокарта — на стороне n8n остаётся только оркестрация.
Как обработать долгие задачи ComfyUI в n8n?
Большинство ComfyUI workflows работают дольше дефолтного таймаута n8n. Используйте асинхронную модель: POST-запрос возвращает task_id, дальше — Wait node + HTTP Request с polling статуса, либо webhook-callback от API на отдельный n8n-trigger.
Что делать при CUDA out of memory в ComfyUI-задаче из n8n?
OOM в managed-API возвращается как ошибка задачи (status=failed, error=cuda_oom). В n8n используйте If node по статусу и retry с уменьшенным разрешением или batch_size. Если запускаете свой ComfyUI — поможет --lowvram режим и tiled VAE.
Где хранить ComfyUI workflow JSON для n8n?
Самый чистый путь — Static Data n8n или внешний Git-репозиторий (n8n умеет читать через HTTP Request). Хранить большие JSON прямо в ноде неудобно — они ломают версионирование.