Skip to content

Instantly share code, notes, and snippets.

@doctorpangloss
Last active February 4, 2025 00:38
Show Gist options
  • Save doctorpangloss/3129533c57793a08e99a3646bdc51def to your computer and use it in GitHub Desktop.
Save doctorpangloss/3129533c57793a08e99a3646bdc51def to your computer and use it in GitHub Desktop.
distributed comfyui example

Below is a succinct guide showing how to set up:

  1. A uv‑backed virtual environment on Linux
  2. A Docker build for ComfyUI
  3. RabbitMQ with a KEDA ScaledObject for auto-scaling workers
  4. A ComfyUI frontend Deployment and ComfyUI worker StatefulSet

…plus a short Python script using the “queue_and_forget” approach.


1. uv Virtual Environment (Linux)

sudo apt-get update
sudo apt-get install -y python3 python3-pip python3-venv git

# Create a workspace and cd into it
mkdir ~/comfyui_workspace
cd ~/comfyui_workspace

# Create a virtual environment with uv (if uv is available)
uv venv --seed --python 3.12
source .venv/bin/activate

# Install required dependencies
uv pip install --upgrade pip setuptools wheel
uv pip install aiohttp
uv pip install "comfyui[withtorch]@git+https://github.com/hiddenswitch/ComfyUI.git"

2. Dockerfile for ComfyUI

Build the image:

docker build -t example.samhodge.com/comfyui:latest .

3. RabbitMQ and KEDA Manifests

Create a file, for instance, rabbitmq-keda.yaml:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rabbitmq
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rabbitmq
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
      containers:
        - name: rabbitmq
          image: rabbitmq:3-management
          ports:
            - containerPort: 5672   # AMQP
            - containerPort: 15672  # Management
---
apiVersion: v1
kind: Service
metadata:
  name: rabbitmq
  namespace: default
spec:
  selector:
    app: rabbitmq
  ports:
    - name: amqp
      port: 5672
      targetPort: 5672
    - name: management
      port: 15672
      targetPort: 15672

# KEDA TriggerAuthentication referencing secret with RabbitMQ connection
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: rabbitmq-trigger-auth
  namespace: default
spec:
  # In a real deployment, you'll have a Secret with this key
  secretTargetRef:
    - parameter: host
      name: rabbitmq-conn-secret
      key: uri  # e.g., "amqp://guest:[email protected]"

# KEDA ScaledObject automatically scales comfyui-worker based on queue length
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: comfyui-scaledobject
  namespace: default
spec:
  scaleTargetRef:
    name: comfyui-worker  # must match the metadata.name of your StatefulSet or Deployment
  minReplicaCount: 1
  maxReplicaCount: 5
  triggers:
    - type: rabbitmq
      metadata:
        queueName: comfyui-queue  # name of queue used by your distributed comfy
        hostFromEnv: host
      authenticationRef:
        name: rabbitmq-trigger-auth

Apply:

kubectl apply -f rabbitmq-keda.yaml

(Make sure you also create a Secret named rabbitmq-conn-secret with key uri containing your RabbitMQ connection string, or adjust the above references accordingly.)


4. ComfyUI Frontend & Worker Manifests

4.1. Frontend (Deployment + Service + Ingress)

Create comfyui-frontend.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: comfyui-frontend
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: comfyui-frontend
  template:
    metadata:
      labels:
        app: comfyui-frontend
    spec:
      containers:
        - name: comfyui-frontend
          image: example.samhodge.com/comfyui:latest
          command: ["/bin/sh", "-c"]
          args:
            - |
              comfyui \
              --distributed-queue-frontend \
              --distributed-queue-connection-uri=amqp://guest:[email protected] \
              --external-address=https://example.samhodge.com
          ports:
            - containerPort: 8188
---
apiVersion: v1
kind: Service
metadata:
  name: comfyui-frontend
  namespace: default
spec:
  selector:
    app: comfyui-frontend
  ports:
    - name: http
      port: 80
      targetPort: 8188
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: comfyui-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    - host: example.samhodge.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: comfyui-frontend
                port:
                  number: 80

4.2. Worker (StatefulSet)

Create comfyui-worker.yaml:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: comfyui-worker
  namespace: default
spec:
  serviceName: "comfyui-worker"
  replicas: 1
  selector:
    matchLabels:
      app: comfyui-worker
  template:
    metadata:
      labels:
        app: comfyui-worker
    spec:
      containers:
        - name: comfyui-worker
          image: example.samhodge.com/comfyui:latest
          command: ["/bin/sh", "-c"]
          args:
            - |
              comfyui-worker \
              --distributed-queue-connection-uri=amqp://guest:[email protected] \
              --external-address=https://example.samhodge.com
          ports:
            - containerPort: 8188

Apply each:

kubectl apply -f comfyui-frontend.yaml
kubectl apply -f comfyui-worker.yaml

5. Short Fire‑and‑Forget Python Script

Create queue_prompt_example.py:

import asyncio
import uuid
from comfy.client.aio_client import AsyncRemoteComfyClient
from comfy.api.components.schema.prompt import PromptDict

# you'll have to fill this in yourself
ADDRESS="http://ingress:8188"

async def main():
    client = AsyncRemoteComfyClient(server_address=ADDRESS, client_id=str(uuid.uuid4()))
    sample_prompt: PromptDict = {
        "1": {
            "class_type": "IntRequestParameter",
            "inputs": {
                "frame": 10
            }
        }
        # ... additional nodes ...
    }

    # Fire-and-forget approach:
    task_id = await client.queue_and_forget_prompt_api(sample_prompt)
    print(f"Prompt queued. Task ID: {task_id}")

if __name__ == "__main__":
    asyncio.run(main())

Running:

python queue_prompt_example.py

You’ll see:

Prompt queued. Task ID: ...

Summary

  • uv venv: fast package install & environment setup on Linux.
  • Docker build: includes ComfyUI.
  • RabbitMQ + KEDA: auto-scales the ComfyUI worker based on queue traffic.
  • Frontend (ingress) & Worker (stateful) for distributed ComfyUI.
  • Fire‑and‑Forget script: shows how to enqueue a prompt quickly without waiting for image bytes.

Enjoy your distributed, auto‑scaling ComfyUI setup!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment