~
2 min read291 words
Authored by Jonte · Verified

Ephemeral GPU Fleet Orchestration for ComfyUI on RunPod

How I built PodBench to parse ComfyUI node graphs into declarative manifests, pre-warm content-addressed safetensors caches, and cut cold-boot GPU provisioning times from 22 minutes to under 90 seconds.

Live URL ↗
TL;DR — KEY POINTS
  • Treat exported ComfyUI workflow JSONs as deterministic lockfiles rather than running imperative setup scripts on boot.
  • Mirror verified safetensors checkpoints to R2 and saturate 10Gbps container links with 16-stream parallel range pulls.
  • Run an in-container NVML + WebSocket queue reaper daemon so idle GPU pods self-terminate with zero orphaned billing.

Local VRAM is finite, and keeping a cloud A100 or RTX 4090 running 24/7 for occasional ComfyUI batch runs is a waste of money. The friction with ephemeral pods has always been state: a single production workflow might reference three checkpoints, four ControlNets, two IP-Adapters, and twelve custom node repositories.

If you rely on manual setup scripts or persistent network volumes locked to a single datacenter region, you either get hit with datacenter GPU stockouts or spend 20+ minutes watching single-threaded Python downloads eat into your billed GPU time.

1. Static Graph Inspection Over Imperative Setup Scripts

When designing PodBench, I treated every ComfyUI workflow JSON as a declarative lockfile. Before calling the RunPod GraphQL API to provision a pod, PodBench walks the node graph, resolves every ckpt_name, lora_name, and unet_name against a SHA-256 model manifest, and generates an exact startup bootstrap plan.

A ComfyUI workflow JSON is already a directed acyclic graph of dependencies. Treating it like a package lockfile eliminates 'missing node / missing safetensors' runtime failures before the GPU instance even boots.

2. Saturating 10Gbps Container Links with Content-Addressed Mirrors

Most cold-boot latency comes from sequentially pulling multi-gigabyte safetensors files over a single TLS stream. By mirroring verified weights into Cloudflare R2 with presigned URLs and saturating the host NIC using parallel range requests (aria2c / hf-transfer with 16 concurrent connections per file), a 24GB checkpoint lands on local NVMe in under 30 seconds.

3. Idle Reaper Daemon & Zero-Orphan Billing

Combined with a lightweight watchdog daemon inside the container that monitors the ComfyUI WebSocket queue and GPU utilization via NVML, pods boot ready to execute the target graph, sync generated outputs back to object storage, and self-terminate the moment the queue stays idle past the configured threshold.

// Reference ComfyUI Graph Dependency Resolver
src/orchestrator/resolveGraphManifest.ts
export function extractWeightsFromGraph(workflow: Record<string, ComfyNode>): ModelRef[] {
  const weights: ModelRef[] = [];
  for (const [nodeId, node] of Object.entries(workflow)) {
    for (const key of ["ckpt_name", "lora_name", "unet_name", "control_net_name"]) {
      const filename = node.inputs?.[key];
      if (typeof filename === "string" && MODEL_LOCK[filename]) {
        weights.push({ nodeId, key, filename, sha256: MODEL_LOCK[filename].sha256 });
      }
    }
  }
  return weights;
}
Ephemeral GPU Fleet Orchestration for ComfyUI on RunPod diagram
Fig 1. Declarative ComfyUI graph parser feeding parallel R2 range-pull hydration and NVML idle pod reaper.

Threat: Missing custom node commits or mismatched safetensors filenames crash batch execution 15 minutes after pod boot.

Control: Pre-flight AST pass resolves every `ckpt_name`, `lora_name`, and custom node Git SHA against a content-addressed manifest before calling RunPod API.