ComfyUI / serverless /README.md
aleph65's picture
qwen-edit-turbo v7: bake Qwen4Play_v2 lora (off by default, lora_qwen4play toggles), drop Amateur-Photo lora; start.sh passes no VRAM flag (full-coverage loras OOM under --highvram; perf unchanged). Image: comfyui-serverless:qwen-edit-turbo-v7
61c37a8 verified
|
Raw
History Blame Contribute Delete
6.79 kB
# ComfyUI serverless workers (RunPod)
Run ComfyUI workflows as RunPod serverless endpoints, optimized for cold start:
**everything is baked into the image** (models included) so a worker whose host
has the image cached starts in seconds and never touches the network.
Images: `plx1029/comfyui-serverless` on Docker Hub. Current endpoint:
| endpoint | image tag | RunPod endpoint id | contents |
|---|---|---|---|
| qwen-edit-turbo | `qwen-edit-turbo-v7` | `dom5lwr0o5wq6u` | Qwen-Image-Edit-2511 **bf16** + Lightning 4/8-step loras + Skin-Fix/Qwen4Play loras, workflow `qwen-edit-turbo-v4` |
| qwen-edit-nsfw | `qwen-edit-nsfw-v1` | `tplcz9jtzihoxa` | Qwen-Rapid-AIO-NSFW v11.4 + next-scene lora, RES4LYF sampler, workflow `qwen-edit-nsfw` |
| faceswap-lissie | `faceswap-lissie-v1` | `qkeibkeoefcrk3` | Flux-2-Klein 9B + Qwen3-8B TE + bfs-head/lissie loras + Qwen3-VL-4B + RMBG-2.0 (baked), workflow `new-faceswap-lissie-v4` |
RunPod templates `serverless-<endpoint>-v1`, endpoints `serverless-<endpoint>`:
H100 80GB (SXM/PCIe/NVL), FlashBoot, min 0 / max 2 workers, idle timeout 60 s,
QUEUE_DELAY scaler. All endpoints are **workflow-agnostic**: they run their
baked workflow by default, any other workflow whose models/nodes are in the
image via `"workflow"`, or a fully custom API-format graph via
`"workflow_json"` (uploaded images are matched to `LoadImage` nodes by name).
Serverless-specific workflow tweaks (baked copies under `endpoints/*/workflows/`
differ from `workflows/api/` in exactly these ways):
- `qwen-edit-turbo` (since `-v7`; `-v1`/`-v2` had Amateur-Photo instead): lora
slot 2 of the "User LoRAs" node is `Qwen4Play_v2`, **off by default**
(params `lora_qwen4play` / `lora_qwen4play_strength` toggle it); the
Amateur-Photo lora is not in the image anymore. Since `-v7`, `start.sh`
passes no VRAM flag (ComfyUI default, same as pods): `--highvram` pinned
lora weight backups in VRAM, and full-coverage loras like Qwen4Play (all
60 blocks) OOM an 80 GB H100 that way. Perf is unchanged (~56 s vs ~57 s
cold execute).
- `qwen-edit-nsfw`: removed the KJNodes `WidgetToString` node (it reads
UI-only metadata and crashes headless); the Image-Saver metadata node gets a
static model-name string instead. Behavior is otherwise identical.
- `faceswap-lissie`: unchanged, but the endpoint defaults `keep_vl_loaded=true`
so Qwen3-VL stays in VRAM between jobs (~10 s faster warm jobs).
## Layout
```
serverless/
β”œβ”€β”€ handler.py # generic RunPod handler (same file for every endpoint)
β”œβ”€β”€ start.sh # container CMD: boot ComfyUI headless, run handler
β”œβ”€β”€ build.sh # daemonless image builder (crane; runs on a pod)
β”œβ”€β”€ Dockerfile # reproducible equivalent for machines with Docker
β”œβ”€β”€ endpoints/
β”‚ └── qwen-edit-turbo/
β”‚ β”œβ”€β”€ endpoint.json # models + custom nodes + default workflow
β”‚ β”œβ”€β”€ workflows/<name>.json # API-format workflow(s) baked into the image
β”‚ └── params/<name>.json # friendly-param -> node.input mapping
β”œβ”€β”€ tests/
β”‚ β”œβ”€β”€ local_test.py # drive handler against a locally running ComfyUI
β”‚ └── endpoint_test.py # drive a deployed RunPod endpoint
└── client/ # standalone caller scripts (stdlib-only) + usage README
β”œβ”€β”€ qwen_edit_sync.py # /runsync: block until done
β”œβ”€β”€ qwen_edit_async.py # /run: submit, poll, fire-and-forget, collect, cancel
└── qwen-edit-turbo-v4.json
```
## Calling the endpoint
```
POST https://api.runpod.ai/v2/<ENDPOINT_ID>/runsync (or /run + /status/<id>)
```
```json
{"input": {
"images": [
{"name": "main.jpg", "image": "<base64 or data-URI>"},
{"name": "ref.png", "image": "<base64>"}
],
"params": {
"prompt": "replace the outfit ...",
"mode": "turbo-8",
"seed": 123,
"input_max_dim": 2048,
"output_max_dim": 2560,
"lora_skin_fix": true
},
"set": {"43.cfg": 1.0}
}}
```
- `images[0]` auto-fills the `image` slot, `images[1]` the `image_ref` slot; a
missing second image automatically sets `use_ref=false`.
- `mode`: `turbo-4` | `turbo-8` (default) | `quality`.
- `seed` omitted β†’ randomized (returned in the response).
- `set` patches any raw `NODE.INPUT` (or `NODE.INPUT.KEY`) after params.
- `workflow` selects among baked workflows; `workflow_json` runs a full
API-format graph passthrough.
- Full param list per workflow: `endpoints/<ep>/params/<workflow>.json`.
Response: `{"images": [{"filename", "type": "base64", "data"}], "seed",
"prompt_id", "workflow", "timings": {...}}`.
Payload limits (RunPod): ~10 MB on `/run`, ~20 MB on `/runsync` β€” enough for
two photos. URL inputs / S3 outputs are a planned follow-up for bigger traffic.
## Building images (no Docker needed β€” run on a pod)
```bash
cd serverless
# heavy tag: base + custom nodes + one layer PER model (build rarely)
./build.sh models qwen-edit-turbo v1
# tiny tag: + handler/workflows/params + CMD (build often, pushes only MBs)
./build.sh code qwen-edit-turbo v1 v1
```
Needs: `crane` + `pigz` installed, models on disk under
`$COMFY_WORKSPACE/ComfyUI/models/`, `DOCKER_HUB_ACCESS_TOKEN` set. The model
layers are content-addressed: rebuilding/pushing a new code version never
re-uploads them. On a machine with Docker, `Dockerfile` builds the same image.
**Adding a new endpoint family** (e.g. krea2): create
`endpoints/krea2/{endpoint.json,workflows/,params/}`, download its models on
the build pod (`download_missing_models.sh <workflows...>`), then
`./build.sh all krea2 v1`. The handler is already generic.
## Cold start design
- Models live in image layers β†’ cached on RunPod host NVMe after first pull;
no network volume, no downloads at start.
- ComfyUI runs headless (`127.0.0.1`, no Manager/frontend extras); its
default memory management keeps the ~55 GB of bf16 weights resident in
VRAM between jobs, so only the first job on a fresh worker pays model
loading (no `--highvram` since v7 β€” see the lora note above).
- FlashBoot on the endpoint snapshots warm workers.
- `SERVERLESS_REFRESH=true` (endpoint env) re-pulls handler/workflows/params
from `hf.co/aleph65/ComfyUI/serverless/` at worker boot β€” iterate on code and
workflow JSONs with **zero image rebuild** (off by default to keep cold
starts fully offline).
## Endpoint env vars
| var | default | meaning |
|---|---|---|
| `SERVERLESS_ENDPOINT` | sole dir in `endpoints/` | which endpoint config to serve |
| `COMFY_PORT` | 7865 | internal ComfyUI port |
| `COMFY_ARGS` | (empty) | extra ComfyUI flags |
| `COMFY_EXEC_TIMEOUT` | 600 | per-job execution timeout (s) |
| `SERVERLESS_REFRESH` | false | boot-time code refresh from the HF repo |