Images go in as base64 under a 10MB ceiling, and a failed S3 upload comes back as an optional error
ComfyUI as a serverless API on Runpod
At a glance
- What is it?
- worker-comfyui packages ComfyUI as a RunPod serverless worker with five Docker image flavours and the standard /run, /runsync and /health endpoints. The response shape broke at version 5.0.0, the request body has a hard size limit that base64 input images hit early, and the quickstart contains no commands at all.
- Who is it for?
- Adopt worker-comfyui if you already run RunPod serverless endpoints and already have workflow JSON in the format ComfyUI exports, because the value here is the packaging and the endpoint contract, not the documentation. Do not adopt it expecting a stable response body: anything written before 5.0.0 read output.message and that field is gone, and a failed S3 upload lands in output.errors as a non fatal warning rather than a failed job.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 11 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Input images are base64 in the request body, under a ceiling the body can hit first
The input object has two required fields and two optional ones. `workflow` carries the ComfyUI graph, `images` is an array where each entry needs a `name` that must be unique within the array plus an `image` holding the base64 string, and a data URI prefix such as `data:image/png;base64,` is accepted but not required. Each uploaded image is written into ComfyUI's own `input` directory and referenced from the graph by that name, which is what a Load Image node looks up. The ceiling is on the request, not the image: the note gives 10MB for `/run` and 20MB for `/runsync`. A base64 payload is larger than the file it encodes, so the point at which an image stops fitting arrives before the image itself reaches the limit. The full shape:
{
"input": {
"workflow": {
"6": {
"inputs": {
"text": "a ball on the table",
"clip": ["30", 1]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
}
},
"images": [
{
"name": "input_image_1.png",
"image": "data:image/png;base64,iVBOR..."
}
]
}
}Version 5.0.0 removed output.message and made upload failures optional
The output contract changed once, hard. Versions below 5.0.0 returned the primary image inside an `output.message` field. From 5.0.0 the result is an object with `output.images`, a list of entries carrying `filename`, `type` and `data`, plus two timings that separate queue delay from execution:
{
"id": "sync-uuid-string",
"status": "COMPLETED",
"output": {
"images": [
{
"filename": "ComfyUI_00001_.png",
"type": "base64",
"data": "iVBORw0KGgoAAAANSUhEUg..."
}
]
},
"delayTime": 123,
"executionTime": 4567
}The part to plan around is `output.errors`, an array of strings present only when something non fatal happened, and the named example in the field table is an S3 upload failure. So a job can report COMPLETED, hand back an images list, and record the fact that the upload never landed, and a client that only reads `output.images` has no signal that anything went wrong. Missing data is listed as the other trigger. That makes `output.errors` a field to check on every call rather than an edge case.
Five image tags, and one of them carries no model at all
The published images all live on Docker Hub under `runpod/worker-comfyui` and are named `<version>-<flavour>`, so choosing an image means choosing both a release and a weight set. `-base` is a clean ComfyUI install with no models. `-flux1-schnell` carries the checkpoint, text encoders and VAE for FLUX.1 schnell, and `-flux1-dev` carries the same three pieces for FLUX.1 dev. `-sdxl` carries a checkpoint and VAEs for Stable Diffusion XL, while `-sd3` carries only a checkpoint for Stable Diffusion 3 medium, with no text encoders or VAE listed for it. A workflow that expects a VAE therefore works on one flavour and fails to load on another, and a base image fails on the first sampler node. No concrete version appears in the README: every tag is written with the literal `<version>` placeholder and the reader is told to check the releases page for the current one.
The requirements file holds three lines, and one of them is an apology
`requirements.txt` opens with a comment reading fix some problems with the queue, then lists `runpod~=1.7.12`, `websocket-client` and `requests`. The comment is the entire explanation of why the RunPod SDK is pinned to that range, and the other two entries carry no version constraint at all. This file is not the worker's whole dependency set: ComfyUI and its Python stack are installed into the image by comfy-cli, and the Dockerfile comment says comfy-cli is pinned on purpose because its install and torch index behaviour decides what lands in the workspace venv, so an unpinned version makes builds non reproducible. That is the real dependency lock, and it lives in the Dockerfile. The Node side is thinner still. `package.json` is marked private, pins `[email protected]` as the package manager, and its only dependency is `@changesets/cli`, used by one script that bumps the version and then runs `node scripts/update-readme-version.js`.
Local development needs an image that Compose refuses to pull
`docker-compose.yml` is a developer convenience, not a deployment. It runs `runpod/worker-comfyui:dev` with `pull_policy: never`, so Compose will not fetch it and the image has to exist locally first, which points at `docker-bake.hcl` as the builder. The service reserves every NVIDIA GPU it can see with `count: all` and the `gpu` capability, sets `SERVE_API_LOCALLY=true`, and publishes two ports: 8000 for the worker and 8188 for the ComfyUI interface itself. Two bind mounts come along, the local `data/comfyui/output` directory mapped onto `/comfyui/output` and `data/runpod-volume` onto `/runpod-volume`. Note that the `dev` tag appears nowhere in the published flavour list, so the local image and the Docker Hub images are built from the same Dockerfile but are not the same thing. Nothing in the quickstart mentions Compose at all.
The image carries an SSH server, a piped uv installer and a pinned ComfyUI version
The Dockerfile starts from `nvidia/cuda:12.8.1-cudnn-runtime-ubuntu24.04` as an overridable build argument and installs `python3.12`, `git`, `wget`, `ffmpeg`, the GL and X libraries ComfyUI needs, and `openssh-server`, which puts an SSH daemon into a serverless worker image. uv is then installed by piping a remote script into a shell and symlinked into `/usr/local/bin`, with a virtual environment created at `/opt/venv` and put first on the path. Four environment variables shape the build: `PIP_PREFER_BINARY=1` to favour wheels, `PYTHONUNBUFFERED=1` so output is not buffered, `CMAKE_BUILD_PARALLEL_LEVEL=8`, and `DEBIAN_FRONTEND=noninteractive`. Two build arguments decide what lands inside: `COMFYUI_VERSION` defaults to 0.34.0 and `ENABLE_PYTORCH_UPGRADE` defaults to false, so the PyTorch build comes from the ComfyUI install unless you flip it.
A per request Comfy.org key overrides the one in the environment
The optional `input.comfy_org_api_key` field carries a Comfy.org API key for API Nodes, and its documented behaviour is precise: it overrides the `COMFY_ORG_API_KEY` environment variable when both are set. That inverts the usual assumption about deployment secrets. A key configured on the worker is a default, not a guarantee, because any caller who supplies one in the request body wins. The credential then travels inside the job payload, which means it is present wherever the request is logged, queued or retried, and it is visible to whoever else can submit jobs to the same endpoint. The rest of the integration surface is read only by comparison: Slack style tokens and service account files are not part of this project at all. The endpoints themselves are the standard RunPod three, `/run`, `/runsync` and `/health`.
The quickstart has no commands, and the licence is AGPL-3.0
The quickstart is five numbered steps containing no runnable command: choose an image, follow the deployment guide in `docs/deployment.md`, optionally configure the worker with environment variables per `docs/configuration.md`, pick an example workflow from `test_resources/workflows/`, then follow the usage steps. The actual commands live in files this repository holds but the opening does not show, and the README stops soon after the output field table, at an empty level one heading. The licence is AGPL-3.0, which is the part of this project with the greatest bearing on a hosting decision, and the repository declares no homepage. What the file does give you is the field tables: the image name and reference rules, the required and optional inputs, the size ceilings, and the output contract with its 5.0.0 note. Read those as the interface, and treat the linked guides as the part that can change without a release.
Editorial conclusion
Adopt worker-comfyui if you already run RunPod serverless endpoints and already have workflow JSON in the format ComfyUI exports, because the value here is the packaging and the endpoint contract, not the documentation. Do not adopt it expecting a stable response body: anything written before 5.0.0 read output.message and that field is gone, and a failed S3 upload lands in output.errors as a non fatal warning rather than a failed job. Before you build anything against it, measure your largest input image against the 10MB and 20MB request ceilings with base64 in mind, decide what the AGPL-3.0 licence means for a hosted deployment, and confirm which image flavour carries the model you need, since the base flavour ships none. The latest tag is 5.11.0 from 2026-09-21.
Frequently asked questions
What shape does a worker-comfyui response have?
Since 5.0.0 the result carries output.images, a list of objects with filename, type and data, plus delayTime and executionTime. Before 5.0.0 the primary image sat in an output.message field, which no longer exists.
Does worker-comfyui tell me when an S3 upload fails?
It records it as a non fatal condition. output.errors is an array of strings present when something such as an S3 upload failure or missing data occurs, while the job itself can still report COMPLETED.
How large can an input image be in worker-comfyui?
The limit is on the whole request: 10MB for /run and 20MB for /runsync. Images are sent as base64 inside input.images, with an optional data URI prefix, and each one is written into ComfyUI's input directory under the name you give it.
Which Docker image flavour should I pick for FLUX.1 or SDXL?
The tags are version plus flavour under runpod/worker-comfyui. flux1-schnell and flux1-dev each include a checkpoint, text encoders and a VAE, sdxl includes a checkpoint and VAEs, sd3 includes only a checkpoint, and base is a clean install with no models at all.
Can I run worker-comfyui locally without a RunPod account?
The shipped compose file is for that: it runs runpod/worker-comfyui:dev with SERVE_API_LOCALLY=true, publishing 8000 and 8188. It sets pull_policy to never, so that image has to be built locally before the stack will start.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/runpod-workers-worker-comfyui)