Skip to content

ComfyUI

Deploy ComfyUI on a Spheron GPU instance using Docker. ComfyUI provides a browser-based node editor for building image generation workflows, plus a JSON API for programmatic access.

Recommended hardware

WorkloadMin VRAMRecommended GPUInstance Type
SD 1.5 workflows6 GBAny 6 GB+ GPUSpot
SDXL workflows10 GBRTX 4090 (24 GB)Spot or Dedicated
FLUX.1 workflows16 GBRTX 4090 (24 GB)Dedicated
Large batch generation40 GBA100 40/80 GBDedicated

Prerequisites

  • A running Spheron GPU instance (see Instance Types)
  • SSH access to the instance
  • A model checkpoint file (.safetensors or .ckpt) for the workflow you want to run

Manual setup

Use these steps to set up the server manually after SSH-ing into your instance. This works on any provider regardless of cloud-init support.

Step 1: Connect to your instance

ssh <user>@<ipAddress>

Replace <user> with the username shown in the instance details panel (e.g., ubuntu for Spheron AI instances) and <ipAddress> with your instance's public IP.

Step 2: Install Docker and NVIDIA container toolkit

sudo apt-get update -y
sudo apt-get install -y docker.io nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

Step 3: Create model and output directories

sudo mkdir -p /opt/comfyui/models /opt/comfyui/output /opt/comfyui/custom_nodes

Place model checkpoints (.safetensors, .ckpt) in /opt/comfyui/models/checkpoints/ before or after startup. ComfyUI auto-detects them.

Step 4: Start ComfyUI

docker run -d \
  --gpus all \
  --name comfyui \
  -p 8188:8188 \
  -v /opt/comfyui/models:/root/ComfyUI/models \
  -v /opt/comfyui/output:/root/ComfyUI/output \
  -v /opt/comfyui/custom_nodes:/root/ComfyUI/custom_nodes \
  yanwk/comfyui-boot:cu128-slim

Verify it is running:

docker ps
docker logs -f comfyui

Accessing the server

SSH tunnel (recommended)

ssh -L 8188:localhost:8188 <user>@<ipAddress>

Then open http://localhost:8188 in your browser.

Programmatic workflow API

ComfyUI exposes a POST /prompt endpoint for running workflows programmatically. Use the browser UI to build and export your workflow as API JSON, then submit it via HTTP.

import requests
import uuid
 
# Export your workflow from ComfyUI browser UI as API format JSON
with open("workflow_api.json") as f:
    import json
    workflow = json.load(f)
 
response = requests.post(
    "http://localhost:8188/prompt",
    json={
        "client_id": str(uuid.uuid4()),
        "prompt": workflow,
    },
)
response.raise_for_status()
prompt_id = response.json()["prompt_id"]
print("Prompt ID:", prompt_id)

Poll for completion

import requests, time
 
prompt_id = "your-prompt-id"
 
# Terminal states per ComfyUI execution.py: "success" or "error"
TERMINAL_ERROR_STATES = {"error"}
 
max_retries = 300  # 5 minutes at 1-second intervals
for _ in range(max_retries):
    response = requests.get(f"http://localhost:8188/history/{prompt_id}")
    response.raise_for_status()
    history = response.json()
    if prompt_id in history:
        job = history[prompt_id]
        status = job.get("status")
        if isinstance(status, dict):
            status_str = status.get("status_str")
        elif status is not None:
            print(f"Warning: unexpected status format (type={type(status).__name__!r}), continuing to poll...")
            status_str = None
        else:
            status_str = None
        if status_str == "success":
            outputs = job.get("outputs", {})
            print("Generation complete:", outputs)
            break
        elif status_str in TERMINAL_ERROR_STATES:
            # Extract the exception message from the execution_error entry in messages
            messages = status.get("messages", []) if isinstance(status, dict) else []
            error_detail = next(
                (msg[1] for msg in messages if isinstance(msg, (list, tuple)) and msg[0] == "execution_error"),
                None,
            )
            detail_str = (
                f": {error_detail.get('exception_message', '')}"
                if isinstance(error_detail, dict)
                else ""
            )
            raise RuntimeError(f"Generation failed with status '{status_str}'{detail_str}")
        elif status_str is not None:
            # Unknown status: log a warning and keep polling
            print(f"Warning: unknown status '{status_str}', continuing to poll...")
    time.sleep(1)
else:
    raise TimeoutError(f"Generation did not complete within {max_retries} seconds")

Check container logs

docker logs -f comfyui

Cloud-init startup script (optional)

If your provider supports cloud-init, you can paste this into the Startup Script field when deploying to automate the setup above. It starts ComfyUI via the yanwk/comfyui-boot:cu128-slim Docker image (CUDA 12.8), a minimal install that includes ComfyUI and ComfyUI-Manager. Install additional custom nodes through the ComfyUI-Manager interface after startup.

#cloud-config
runcmd:
  - apt-get update -y
  - apt-get install -y docker.io nvidia-container-toolkit
  - nvidia-ctk runtime configure --runtime=docker
  - systemctl restart docker
  - mkdir -p /opt/comfyui/models /opt/comfyui/output /opt/comfyui/custom_nodes
  - |
    docker run -d \
      --gpus all \
      --name comfyui \
      -p 8188:8188 \
      -v /opt/comfyui/models:/root/ComfyUI/models \
      -v /opt/comfyui/output:/root/ComfyUI/output \
      -v /opt/comfyui/custom_nodes:/root/ComfyUI/custom_nodes \
      yanwk/comfyui-boot:cu128-slim

Place model checkpoints (.safetensors, .ckpt) in /opt/comfyui/models/checkpoints/ before or after startup.

What's next