Initial commit — ComfyUI on NVIDIA fronted by Open WebUI

Replaces the figment/segment/Forge stack with a single ComfyUI backend
fronted by Open WebUI's native ComfyUI integration. ComfyUI is built from
the official manual install for NVIDIA. Ships txt2img and img2img workflow
templates plus matching node-mapping JSONs that paste into Open WebUI's
admin panel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-19 10:21:43 -05:00
co-authored by Claude Opus 4.7
commit a73aa4a38a
10 changed files with 491 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
# Copy to .env and edit. docker compose picks .env up automatically.
# Public hostname/scheme your reverse proxy terminates. Used by Open WebUI for
# auth redirects and email-link generation.
OPEN_WEBUI_URL=http://localhost:3000
# Cookie-signing key. Generate once with: openssl rand -hex 32
# Must stay stable across restarts — rotating it logs every user out.
OPEN_WEBUI_SECRET_KEY=change-me-please
+5
View File
@@ -0,0 +1,5 @@
.env
*.log
.idea/
.vscode/
__pycache__/
+59
View File
@@ -0,0 +1,59 @@
# ComfyUI on NVIDIA — manual install per
# https://docs.comfy.org/installation/manual_install#nvidia
#
# Base image provides system CUDA / cuDNN libs that some custom nodes dlopen at
# runtime. The PyTorch wheels installed below bundle their own CUDA runtime —
# the base only needs to be ABI-compatible with the host driver via
# nvidia-container-toolkit.
#
# CUDA 12.6 + cu126 wheels chosen for broad driver compatibility (driver >=
# 545). If the host driver is >= 580, switch the wheel index URL to cu130 for
# the newest stable PyTorch.
FROM nvidia/cuda:12.6.3-cudnn-runtime-ubuntu24.04
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
COMFYUI_HOME=/opt/comfyui
RUN apt-get update && apt-get install -y --no-install-recommends \
python3.12 \
python3.12-venv \
python3-pip \
git \
ca-certificates \
curl \
libgl1 \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
# Pin a venv so future pip installs (custom nodes) stay isolated.
RUN python3.12 -m venv /opt/venv
ENV PATH=/opt/venv/bin:$PATH
# Upstream wheel for NVIDIA. cu126 covers driver >= 545; bump to cu130 for
# driver >= 580.
RUN pip install --upgrade pip && \
pip install torch torchvision torchaudio \
--extra-index-url https://download.pytorch.org/whl/cu126
# Pull ComfyUI itself. Pinning to a tag would be safer for reproducibility but
# the project ships breaking changes infrequently and most users want latest.
RUN git clone --depth 1 https://github.com/comfyanonymous/ComfyUI.git ${COMFYUI_HOME}
WORKDIR ${COMFYUI_HOME}
RUN pip install -r requirements.txt
# ComfyUI-Manager — community node manager. Lets users install/update custom
# nodes from the web UI instead of editing the Dockerfile. Remove this block if
# you want a pristine ComfyUI.
RUN git clone --depth 1 https://github.com/ltdrdata/ComfyUI-Manager.git \
${COMFYUI_HOME}/custom_nodes/ComfyUI-Manager && \
pip install -r ${COMFYUI_HOME}/custom_nodes/ComfyUI-Manager/requirements.txt
EXPOSE 8188
# --listen 0.0.0.0 binds to every interface so the Open WebUI container on the
# shared compose network can reach it. --port is explicit for clarity.
CMD ["python", "main.py", "--listen", "0.0.0.0", "--port", "8188"]
+36
View File
@@ -0,0 +1,36 @@
# comfyui-nvidia
ComfyUI image-generation backend, NVIDIA-accelerated, fronted by Open WebUI for
multi-user chat and image generation/editing.
Built from the official ComfyUI [manual install for
NVIDIA](https://docs.comfy.org/installation/manual_install#nvidia) — no third-
party base image. Two services on one bridge network:
| Service | Port (host) | Role |
| ----------- | ----------- | ----------------------------------------------- |
| `comfyui` | `8188` | ComfyUI server + native web UI |
| `open-webui`| `3000` | Multi-user chat with txt2img / img2img panel |
Open WebUI submits ComfyUI workflows directly via the documented integration —
no MCP shim, no API translation layer. Workflow templates live in
[`workflows/`](workflows/) and get pasted into Open WebUI's admin panel during
setup.
## Quick start
```sh
cp .env.example .env
# edit .env — set OPEN_WEBUI_SECRET_KEY at minimum
docker compose up -d --build
```
Then drop a checkpoint into the `comfyui-models` volume and finish the Open
WebUI ComfyUI configuration. Full walkthrough in [SETUP.md](SETUP.md).
## Replaces
This repo supersedes the previous figment + segment + Forge stack. ComfyUI's
node graph covers everything those services provided (txt2img, img2img,
inpaint, mask generation via SAM/GroundingDINO custom nodes), and Open WebUI
talks to it natively.
+135
View File
@@ -0,0 +1,135 @@
# Setup
End-to-end walkthrough: clean host -> running stack -> first generated image
in Open WebUI.
## 1. Host prerequisites
- Linux (or WSL2) with an NVIDIA GPU and a recent driver.
- cu126 wheels (default Dockerfile): driver >= 545
- cu130 wheels (swap in Dockerfile): driver >= 580
- Docker Engine + Compose v2.
- [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
installed and the Docker runtime configured (`nvidia-ctk runtime configure
--runtime=docker && systemctl restart docker`).
Confirm GPU passthrough works before bringing the stack up:
```sh
docker run --rm --gpus all nvidia/cuda:12.6.3-base-ubuntu24.04 nvidia-smi
```
## 2. Configure environment
```sh
cp .env.example .env
```
Edit `.env`:
- `OPEN_WEBUI_URL` — public URL Open WebUI is reached at (used for auth
redirects). For local-only, leave the default.
- `OPEN_WEBUI_SECRET_KEY` — generate with `openssl rand -hex 32`. Keep stable;
rotating it logs every user out.
## 3. Build and start
```sh
docker compose up -d --build
```
First build pulls the CUDA base, PyTorch wheels, and ComfyUI source — expect
several minutes. `docker compose logs -f comfyui` should end with a line like
`To see the GUI go to: http://0.0.0.0:8188`.
Health-check both services:
```sh
curl -sf http://localhost:8188/system_stats | head -c 200
curl -sf http://localhost:3000/health
```
## 4. Add at least one checkpoint
ComfyUI ships no models. The shipped workflow templates reference
`v1-5-pruned-emaonly.safetensors` as a placeholder; drop any SD/SDXL/Flux
checkpoint into the `comfyui-models` volume under `checkpoints/`:
```sh
docker run --rm -v comfyui-nvidia_comfyui-models:/models -w /models/checkpoints \
curlimages/curl:latest -L -O \
https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors
```
Or open the ComfyUI web UI at <http://localhost:8188>, click the **Manager**
button (added by ComfyUI-Manager), and use **Model Manager** to install one
through the UI.
## 5. First-user signup in Open WebUI
Open <http://localhost:3000>. The first account created becomes the admin.
Subsequent signups land in `pending` and need admin approval (set by
`DEFAULT_USER_ROLE: pending` in compose).
## 6. Wire Open WebUI to ComfyUI
Open WebUI ships the ComfyUI integration but won't know which workflow to
submit until you paste one in. Do this once per workflow (txt2img and
img2img).
In Open WebUI: **Admin Panel -> Settings -> Images**.
1. **Image Generation Engine** -> `ComfyUI` (already preselected via env var).
2. **ComfyUI Base URL** -> `http://comfyui:8188` (already preselected).
3. **ComfyUI Workflow** -> paste the entire contents of
[`workflows/txt2img.json`](workflows/txt2img.json).
4. **ComfyUI Workflow Nodes** -> paste the contents of
[`workflows/txt2img.nodes.json`](workflows/txt2img.nodes.json).
5. **Default Model** -> the filename of the checkpoint you dropped in step 4
(e.g. `v1-5-pruned-emaonly.safetensors`).
6. Save.
For image editing (img2img), scroll to the **Image Editing** section in the
same panel and repeat with [`workflows/img2img.json`](workflows/img2img.json)
and [`workflows/img2img.nodes.json`](workflows/img2img.nodes.json).
## 7. Test it
In any chat, click the image-generation button and prompt for an image. Open
WebUI submits the workflow to ComfyUI; the result drops back into the chat
when KSampler finishes. To test img2img, attach an image and use the edit
action.
## How the workflow node mappings work
Open WebUI doesn't introspect the workflow graph. The `*.nodes.json` files
tell it which node IDs and input fields to overwrite when the user provides
a prompt, image, seed, etc. Each entry:
```json
{ "type": "<placeholder>", "node_ids": ["<id>"], "key": "<input field>" }
```
Recognised `type` strings (per Open WebUI source): `model`, `prompt`,
`negative_prompt`, `width`, `height`, `n` (batch size), `steps`, `seed`, and
`image` (img2img / edit only).
If you swap in a fancier workflow (SDXL, Flux, ControlNet, custom samplers,
NL masking via SAM nodes, etc.), update the matching `*.nodes.json` so the
node IDs and input keys still line up.
## Common gotchas
- **"Model not found" in Open WebUI's image panel.** ComfyUI lists models from
`/opt/comfyui/models/checkpoints/`. Confirm the file is there and matches
the **Default Model** field exactly (filename including extension).
- **Out-of-memory on first generate.** Lower `IMAGE_SIZE` in compose
(e.g. `768x768`) or pass `--lowvram` / `--medvram` in the Dockerfile CMD.
- **Custom nodes need extra pip packages.** Install via ComfyUI-Manager (it
pip-installs into the container's venv). Persisted because
`/opt/comfyui/custom_nodes` is a named volume — but the venv at `/opt/venv`
is not, so packages added by the manager survive container restarts only if
the manager re-installs them on boot. For permanent custom-node deps, add a
`RUN pip install ...` to the Dockerfile and rebuild.
- **GPU not visible inside container.** Re-run the `nvidia-smi` test in step
1. If it fails, the toolkit is misconfigured.
+106
View File
@@ -0,0 +1,106 @@
# comfyui-nvidia — ComfyUI image-generation backend fronted by Open WebUI.
#
# Open WebUI provides the multi-user chat UI and a built-in image-generation
# panel that submits ComfyUI workflows directly. Two workflow templates ship in
# workflows/ — txt2img.json and img2img.json. Paste them (and the matching
# node-mapping JSON) into the Open WebUI admin panel under
# Settings -> Images. See SETUP.md.
#
# Single GPU, single host. Both services share the comfyui-net bridge so Open
# WebUI addresses ComfyUI by service name (http://comfyui:8188).
name: comfyui-nvidia
networks:
comfyui-net:
driver: bridge
volumes:
comfyui-models: # /opt/comfyui/models (checkpoints, vae, loras, ...)
comfyui-custom-nodes: # /opt/comfyui/custom_nodes
comfyui-input: # /opt/comfyui/input (uploaded source images)
comfyui-output: # /opt/comfyui/output (generated images)
comfyui-user: # /opt/comfyui/user (saved workflows, settings)
open-webui-data: # Open WebUI accounts, chats, settings
services:
comfyui:
build:
context: .
dockerfile: Dockerfile
image: comfyui-nvidia:local
container_name: comfyui
restart: unless-stopped
networks: [comfyui-net]
ports:
# Optional on the host — Open WebUI reaches comfyui:8188 over the bridge.
# Keep it published if you also want to use ComfyUI's native web UI at
# http://localhost:8188.
- "8188:8188"
volumes:
- comfyui-models:/opt/comfyui/models
- comfyui-custom-nodes:/opt/comfyui/custom_nodes
- comfyui-input:/opt/comfyui/input
- comfyui-output:/opt/comfyui/output
- comfyui-user:/opt/comfyui/user
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
healthcheck:
test: ["CMD", "curl", "-sf", "http://127.0.0.1:8188/system_stats"]
interval: 30s
timeout: 5s
retries: 5
start_period: 120s
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
networks: [comfyui-net]
ports:
- "3000:8080"
extra_hosts:
# So Open WebUI can reach an Ollama instance running on the VM host.
# Drop this if you don't run Ollama on the host.
- "host.docker.internal:host-gateway"
depends_on:
comfyui:
condition: service_started
environment:
WEBUI_AUTH: "true"
ENABLE_SIGNUP: "true"
DEFAULT_USER_ROLE: "pending"
# Public URL used for auth redirects and email-link generation. Set this
# to whatever your reverse proxy terminates.
WEBUI_URL: "${OPEN_WEBUI_URL:-http://localhost:3000}"
# Cookie-signing key. Stable across restarts (rotating it logs every
# user out). Generate once: openssl rand -hex 32
WEBUI_SECRET_KEY: "${OPEN_WEBUI_SECRET_KEY:-change-me-please}"
# Optional: Ollama on the VM host for chat. Comment out if unused.
OLLAMA_BASE_URL: "http://host.docker.internal:11434"
# Image generation via ComfyUI. The workflow JSON and node-mapping JSON
# are configured through the admin UI — see SETUP.md for the paste-in
# values. Only the engine selection and base URL are wired here so the
# admin panel comes up pre-pointed at the right backend.
ENABLE_IMAGE_GENERATION: "true"
IMAGE_GENERATION_ENGINE: "comfyui"
COMFYUI_BASE_URL: "http://comfyui:8188"
IMAGE_SIZE: "1024x1024"
IMAGE_STEPS: "20"
volumes:
- open-webui-data:/app/backend/data
healthcheck:
test: ["CMD", "curl", "-sf", "http://127.0.0.1:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s
+64
View File
@@ -0,0 +1,64 @@
{
"3": {
"class_type": "KSampler",
"inputs": {
"seed": 0,
"steps": 20,
"cfg": 7,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 0.75,
"model": ["4", 0],
"positive": ["6", 0],
"negative": ["7", 0],
"latent_image": ["10", 0]
}
},
"4": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "v1-5-pruned-emaonly.safetensors"
}
},
"6": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "",
"clip": ["4", 1]
}
},
"7": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "",
"clip": ["4", 1]
}
},
"8": {
"class_type": "VAEDecode",
"inputs": {
"samples": ["3", 0],
"vae": ["4", 2]
}
},
"9": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "ComfyUI",
"images": ["8", 0]
}
},
"10": {
"class_type": "VAEEncode",
"inputs": {
"pixels": ["11", 0],
"vae": ["4", 2]
}
},
"11": {
"class_type": "LoadImage",
"inputs": {
"image": "example.png"
}
}
}
+8
View File
@@ -0,0 +1,8 @@
[
{ "type": "model", "node_ids": ["4"], "key": "ckpt_name" },
{ "type": "prompt", "node_ids": ["6"], "key": "text" },
{ "type": "negative_prompt", "node_ids": ["7"], "key": "text" },
{ "type": "steps", "node_ids": ["3"], "key": "steps" },
{ "type": "seed", "node_ids": ["3"], "key": "seed" },
{ "type": "image", "node_ids": ["11"], "key": "image" }
]
+59
View File
@@ -0,0 +1,59 @@
{
"3": {
"class_type": "KSampler",
"inputs": {
"seed": 0,
"steps": 20,
"cfg": 7,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1,
"model": ["4", 0],
"positive": ["6", 0],
"negative": ["7", 0],
"latent_image": ["5", 0]
}
},
"4": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "v1-5-pruned-emaonly.safetensors"
}
},
"5": {
"class_type": "EmptyLatentImage",
"inputs": {
"width": 1024,
"height": 1024,
"batch_size": 1
}
},
"6": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "",
"clip": ["4", 1]
}
},
"7": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "",
"clip": ["4", 1]
}
},
"8": {
"class_type": "VAEDecode",
"inputs": {
"samples": ["3", 0],
"vae": ["4", 2]
}
},
"9": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "ComfyUI",
"images": ["8", 0]
}
}
}
+10
View File
@@ -0,0 +1,10 @@
[
{ "type": "model", "node_ids": ["4"], "key": "ckpt_name" },
{ "type": "prompt", "node_ids": ["6"], "key": "text" },
{ "type": "negative_prompt", "node_ids": ["7"], "key": "text" },
{ "type": "width", "node_ids": ["5"], "key": "width" },
{ "type": "height", "node_ids": ["5"], "key": "height" },
{ "type": "n", "node_ids": ["5"], "key": "batch_size" },
{ "type": "steps", "node_ids": ["3"], "key": "steps" },
{ "type": "seed", "node_ids": ["3"], "key": "seed" }
]