This commit is contained in:
Anatoly Butko 2025-06-28 21:44:26 -04:00
commit 3d92e6f72e
12 changed files with 687 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.vscode
ComfyUI
custom_nodes
input
output
models

182
README.md Normal file
View File

@ -0,0 +1,182 @@
# ComfyUI ROCm Docker Image
🔥 **ComfyUI with AMD ROCm support** - Run ComfyUI on AMD GPUs with optimized ROCm-compatible dependencies.
[![Docker Pulls](https://img.shields.io/docker/pulls/corundex/comfyui-rocm)](https://hub.docker.com/r/corundex/comfyui-rocm) [![ROCm](https://img.shields.io/badge/ROCm-6.4+-green)](https://rocm.docs.amd.com/) [![AMD GPU](https://img.shields.io/badge/AMD-RX%206000%2B-red)](https://www.amd.com/en/graphics/radeon-rx-graphics)
![ComfyUI Interface](Screenshot.png)
*ComfyUI running on AMD ROCm with sample workflow and generated landscape image*
## 📋 Version Information
- **Base Image**: `rocm/pytorch:rocm6.4.1_ubuntu24.04_py3.12_pytorch_release_2.6.0`
- **Python**: 3.12.10
- **PyTorch**: 2.6.0+git684f6f2
- **ROCm**: 6.4.43483-a187df25c
- **ComfyUI**: v0.3.43 (e18f53c, 2025-06-27)
## ✨ Key Features
- 🎨 **Node-based AI workflow** - Visual interface for creating complex AI pipelines
- 🔥 **AMD ROCm optimized** - Native AMD GPU acceleration with ROCm 6.4+
- 📦 **Smart model management** - Automatic downloads with configurable model sets
- 🧪 **Tested compatibility** - All dependencies verified on real AMD hardware
- 🎯 **Ready to use** - Pre-configured with sample workflows
- 💾 **Persistent storage** - Models and outputs preserved across restarts
## 🚀 Quick Start
```bash
# Pull and run ComfyUI with ROCm support
docker run -d \
--device=/dev/kfd \
--device=/dev/dri \
--group-add=video \
-p 8188:8188 \
-v $(pwd)/models:/workspace/ComfyUI/models \
-v $(pwd)/output:/workspace/ComfyUI/output \
corundex/comfyui-rocm:latest
```
Access ComfyUI at: **http://localhost:8188**
## 📋 Requirements
| Component | Requirement |
|-----------|-------------|
| **GPU** | AMD RX 6000/7000+ series with ROCm support |
| **VRAM** | 8GB minimum (16GB+ recommended) |
| **OS** | Linux (Ubuntu 24.04+ recommended) |
| **Docker** | Latest version with GPU support |
| **ROCm** | Drivers 6.4+ installed on host |
## 🔧 Setup Instructions
### 1. Install ROCm Drivers
```bash
# Ubuntu/Debian
curl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/rocm.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/6.4 jammy main" | sudo tee /etc/apt/sources.list.d/rocm.list
sudo apt update && sudo apt install rocm-dkms
sudo usermod -a -G render,video $USER
```
### 2. Verify ROCm Installation
```bash
rocm-smi # Should show your AMD GPU(s)
```
### 3. Run ComfyUI
```bash
docker run -d \
--name comfyui-rocm \
--device=/dev/kfd --device=/dev/dri --group-add=video \
-p 8188:8188 \
-v ./models:/workspace/ComfyUI/models \
-v ./output:/workspace/ComfyUI/output \
corundex/comfyui-rocm:latest
```
## 🎛️ Model Management
Control model downloading with the `MODEL_DOWNLOAD` environment variable:
| Mode | Description | Models Included |
|------|-------------|-----------------|
| `default` | Essential starter (4GB) | SD 1.5 |
| `common` | Comprehensive set (~30GB) | SD 1.5, SDXL, ControlNets, upscalers, VAE, embeddings |
| `realistic` | Photo-realistic models (~8GB) | Realistic Vision, DreamShaper, VAE |
| `photorealistic` | SDXL realistic (~12GB) | Juggernaut XL, RealVisXL |
| `artistic` | Creative/stylized (~2GB) | Deliberate v2 |
| `all` | Everything (~100GB) | All model sets combined |
| `none` | Skip downloads | Use existing models only |
### Usage Examples
```bash
# Default models (SD 1.5)
docker run -d --device=/dev/kfd --device=/dev/dri --group-add=video \
-p 8188:8188 -v ./models:/workspace/ComfyUI/models \
corundex/comfyui-rocm:latest
# All models (~100GB download)
docker run -d --device=/dev/kfd --device=/dev/dri --group-add=video \
-p 8188:8188 -e MODEL_DOWNLOAD=all \
-v ./models:/workspace/ComfyUI/models \
corundex/comfyui-rocm:latest
# Use existing models only
docker run -d --device=/dev/kfd --device=/dev/dri --group-add=video \
-p 8188:8188 -e MODEL_DOWNLOAD=none \
-v ./models:/workspace/ComfyUI/models \
corundex/comfyui-rocm:latest
```
## 🐳 Docker Compose
```yaml
services:
comfyui-rocm:
image: corundex/comfyui-rocm:latest
container_name: comfyui-rocm
devices:
- /dev/kfd:/dev/kfd
- /dev/dri:/dev/dri
group_add:
- video
ports:
- "8188:8188"
volumes:
- ./data/models:/workspace/ComfyUI/models
- ./data/output:/workspace/ComfyUI/output
- ./data/input:/workspace/ComfyUI/input
- ./data/custom_nodes:/workspace/ComfyUI/custom_nodes
- ./data/user:/workspace/ComfyUI/user
environment:
- MODEL_DOWNLOAD=default
- HIP_VISIBLE_DEVICES=0
- CUDA_VISIBLE_DEVICES=""
restart: unless-stopped
```
Run with: `docker compose up -d`
## ⚡ Performance & Hardware
### Tested Hardware
- **AMD Radeon RX 9060 XT** (16GB VRAM) ✅
### Performance Metrics
- **Generation Time**: ~30-60s for 512x512 images
- **VRAM Usage**: 4-8GB for basic operations
- **Model Loading**: ~30-60s first time, cached afterward
- **Batch Processing**: Multiple images supported
### Tips
- Mount persistent volumes to avoid re-downloading models
- Start with `default` models, upgrade to larger sets as needed
- Use fast SSD storage for optimal performance
## 🔍 Troubleshooting
| Issue | Solution |
|-------|----------|
| **Container won't start** | Check ROCm drivers: `rocm-smi` |
| **No GPU detected** | Verify container GPU access: `docker exec comfyui-rocm python -c "import torch; print(torch.cuda.is_available())"` |
| **Model download fails** | Check internet connection, disk space, and logs |
| **Out of memory** | Reduce batch size, use smaller models, ensure 8GB+ VRAM |
| **Models not found** | Verify downloads completed and file permissions |
## 📄 License & Credits
This Docker image packages ComfyUI with ROCm support. ComfyUI is licensed under GPL-3.0.
**Acknowledgments:**
- [ComfyUI](https://github.com/comfyanonymous/ComfyUI) - Node-based AI workflow interface
- [AMD ROCm](https://rocm.docs.amd.com/) - Open source GPU computing platform
- ROCm community for AMD GPU AI support
---
🔗 **Links:** [Docker Hub](https://hub.docker.com/r/corundex/comfyui-rocm) | [GitHub](https://github.com/corundex/comfyui_rocm) | [ComfyUI](https://github.com/comfyanonymous/ComfyUI)

BIN
Screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

17
build.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
# build.sh - Build ComfyUI ROCm Docker image
set -e
# Configuration
IMAGE_NAME="comfyui-rocm"
VERSION="${1:-latest}"
DETAILED_TAG="comfyui_0.3.43__rocm6.4.1_ubuntu24.04_py3.12_pytorch_2.6.0"
echo "🐳 Building ComfyUI ROCm Docker image..."
echo "📦 Tags: ${IMAGE_NAME}:${VERSION}, ${IMAGE_NAME}:${DETAILED_TAG}"
echo ""
# Build the image with both tags
echo "🔨 Building Docker image..."
docker build -f docker/Dockerfile -t "${IMAGE_NAME}:${VERSION}" -t "${IMAGE_NAME}:${DETAILED_TAG}" --progress=plain .

53
docker-compose.yaml Normal file
View File

@ -0,0 +1,53 @@
services:
comfyui-rocm:
image: corundex/comfyui-rocm:latest
pull_policy: always
container_name: comfyui-rocm
hostname: comfyui-rocm
# GPU access for AMD ROCm
devices:
- /dev/kfd:/dev/kfd
- /dev/dri:/dev/dri
# Add video group for GPU access
group_add:
- video
# Port mapping
ports:
- "8188:8188"
# Volume mapping to preserve models and data
volumes:
# Model storage (checkpoints, VAE, LoRA, etc.)
- ./data/models:/workspace/ComfyUI/models
# Generated images and outputs
- ./data/output:/workspace/ComfyUI/output
# Input images for processing
- ./data/input:/workspace/ComfyUI/input
# Custom nodes and extensions
- ./data/custom_nodes:/workspace/ComfyUI/custom_nodes
# ComfyUI user settings and workflows
- ./data/user:/workspace/ComfyUI/user
# Temporary files (optional)
- ./data/temp:/workspace/ComfyUI/temp
# Environment variables
environment:
# Model download behavior: default, common, realistic, photorealistic, artistic, all, none
- MODEL_DOWNLOAD=default
# ROCm environment
- HIP_VISIBLE_DEVICES=0
- CUDA_VISIBLE_DEVICES=""
# Restart policy
restart: unless-stopped
# Healthcheck
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8188/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 120s

3
docker/.dockerignore Normal file
View File

@ -0,0 +1,3 @@
# Git
.git
.gitignore

68
docker/Dockerfile Normal file
View File

@ -0,0 +1,68 @@
# ROCm ComfyUI Dockerfile
# Based on AMD's official ROCm PyTorch container
FROM rocm/pytorch:rocm6.4.1_ubuntu24.04_py3.12_pytorch_release_2.6.0
# Set working directory
WORKDIR /workspace
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
wget \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Clone ComfyUI
RUN git clone https://github.com/comfyanonymous/ComfyUI.git
# Set ComfyUI as working directory
WORKDIR /workspace/ComfyUI
# Create necessary directories with separate commands
RUN mkdir -p /workspace/ComfyUI/models/checkpoints && \
mkdir -p /workspace/ComfyUI/models/vae && \
mkdir -p /workspace/ComfyUI/models/loras && \
mkdir -p /workspace/ComfyUI/models/embeddings && \
mkdir -p /workspace/ComfyUI/models/upscale_models && \
mkdir -p /workspace/ComfyUI/models/controlnet && \
mkdir -p /workspace/ComfyUI/output && \
mkdir -p /workspace/ComfyUI/input && \
mkdir -p /workspace/ComfyUI/custom_nodes
# Copy ROCm-tested requirements files
COPY docker/requirements_rocm.txt /workspace/
# Install Python dependencies using ROCm-compatible requirements
# These files have been tested on real AMD hardware and filter out packages that break ROCm
RUN pip install --no-cache-dir -r /workspace/requirements_rocm.txt
# Copy startup script, models config, and sample workflow
COPY docker/startup.sh /workspace/startup.sh
COPY docker/download_models.py /workspace/download_models.py
COPY docker/models.yaml /workspace/models.yaml
COPY docker/sample_workflow.json /workspace/ComfyUI/
# Make startup script executable
RUN chmod +x /workspace/startup.sh
# Environment variables for model download behavior
# MODEL_DOWNLOAD options:
# "default" - Download basic SD 1.5 model (default)
# "all" - Download full model set (SD 1.5, SDXL, ControlNet, etc.)
# "realistic" - Download realistic photo models
# "none" - Skip all downloads, use existing models only
# Custom - Any section name from models.conf
ENV MODEL_DOWNLOAD=default
# Expose ComfyUI port
EXPOSE 8188
# Health check to ensure ComfyUI is running
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8188/ || exit 1
# Use smart startup script that handles model downloads
CMD ["/workspace/startup.sh"]

131
docker/download_models.py Normal file
View File

@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""
Smart model downloader for ComfyUI
"""
import os
import sys
import yaml
import requests
from pathlib import Path
from urllib.parse import urlparse
def log(message):
print(f"[ComfyUI] {message}", flush=True)
def download_file(url, filepath, name, min_size=0):
"""Download file with progress bar and validation"""
if filepath.exists():
if filepath.stat().st_size >= min_size:
log(f"{name} already exists, skipping")
return True
else:
log(f"{name} exists but too small, redownloading")
filepath.unlink()
log(f"Downloading {name}...")
log(f"URL: {url}")
log(f"Path: {filepath}")
# Create directory if needed
filepath.parent.mkdir(parents=True, exist_ok=True)
try:
response = requests.get(url, stream=True)
response.raise_for_status()
with open(filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
# Validate file size
if filepath.stat().st_size >= min_size:
log(f"{name} downloaded successfully")
return True
else:
log(f"{name} download failed - file too small")
filepath.unlink()
return False
except Exception as e:
log(f"{name} download failed: {e}")
if filepath.exists():
filepath.unlink()
return False
def load_models_config(config_path):
"""Load models configuration from YAML"""
try:
with open(config_path, 'r') as f:
return yaml.safe_load(f)
except Exception as e:
log(f"Failed to load config {config_path}: {e}")
return None
def download_model_set(models_config, model_set, models_base):
"""Download a specific set of models"""
if model_set == 'all':
# Special case: download all model sets except 'all' itself
log("Downloading ALL model sets...")
all_success = True
for key in models_config['models']:
if key != 'all': # Skip 'all' to avoid recursion
log(f"Processing model set: {key}")
if not download_model_set(models_config, key, models_base):
all_success = False
log(f"All model sets completed: {'SUCCESS' if all_success else 'PARTIAL'}")
return all_success
if model_set not in models_config['models']:
log(f"Unknown model set: {model_set}")
return False
models = models_config['models'][model_set]
log(f"Downloading {model_set} models ({len(models)} total)...")
success_count = 0
for model in models:
filepath = Path(models_base) / model['path']
min_size = model.get('min_size', 100000000) # 100MB default
if download_file(model['url'], filepath, model['name'], min_size):
success_count += 1
log(f"{model_set} downloads completed: {success_count}/{len(models)} successful")
return success_count == len(models)
def main():
models_base = "/workspace/ComfyUI/models"
config_path = "/workspace/models.yaml"
model_download = os.environ.get('MODEL_DOWNLOAD', 'default')
log(f"Model downloader starting (MODEL_DOWNLOAD={model_download})")
# Create model directories
for subdir in ['checkpoints', 'vae', 'loras', 'upscale_models', 'controlnet', 'embeddings']:
Path(models_base, subdir).mkdir(parents=True, exist_ok=True)
if model_download == 'none':
log("Skipping model downloads (MODEL_DOWNLOAD=none)")
return True
# Load configuration
models_config = load_models_config(config_path)
if not models_config:
log("Failed to load models configuration, skipping downloads")
return False
# Download models
success = download_model_set(models_config, model_download, models_base)
if success:
log("Model downloader completed successfully")
else:
log("Model downloader completed with some failures (continuing anyway)")
# Always return success to avoid restart loops
return True
if __name__ == '__main__':
success = main()
sys.exit(0 if success else 1)

79
docker/models.yaml Normal file
View File

@ -0,0 +1,79 @@
models:
default:
- name: "Stable Diffusion 1.5"
url: "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors"
path: "checkpoints/v1-5-pruned-emaonly.safetensors"
min_size: 4000000000
common:
- name: "Stable Diffusion 1.5"
url: "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors"
path: "checkpoints/v1-5-pruned-emaonly.safetensors"
min_size: 4000000000
- name: "SD 1.5 VAE"
url: "https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors"
path: "vae/vae-ft-mse-840000-ema-pruned.safetensors"
min_size: 300000000
- name: "SDXL Base 1.0"
url: "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors"
path: "checkpoints/sd_xl_base_1.0.safetensors"
min_size: 6000000000
- name: "SDXL Refiner 1.0"
url: "https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/resolve/main/sd_xl_refiner_1.0.safetensors"
path: "checkpoints/sd_xl_refiner_1.0.safetensors"
min_size: 6000000000
- name: "ControlNet Canny"
url: "https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_canny.pth"
path: "controlnet/control_v11p_sd15_canny.pth"
min_size: 1400000000
- name: "ControlNet Depth"
url: "https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11f1p_sd15_depth.pth"
path: "controlnet/control_v11f1p_sd15_depth.pth"
min_size: 1400000000
- name: "ControlNet OpenPose"
url: "https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.pth"
path: "controlnet/control_v11p_sd15_openpose.pth"
min_size: 1400000000
- name: "Real-ESRGAN 4x Upscaler"
url: "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth"
path: "upscale_models/RealESRGAN_x4plus.pth"
min_size: 60000000
- name: "Real-ESRGAN 4x Anime"
url: "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth"
path: "upscale_models/RealESRGAN_x4plus_anime_6B.pth"
min_size: 15000000
- name: "EasyNegative Embedding"
url: "https://huggingface.co/datasets/gsdf/EasyNegative/resolve/main/EasyNegative.safetensors"
path: "embeddings/EasyNegative.safetensors"
min_size: 20000
realistic:
- name: "Realistic Vision v5.1"
url: "https://huggingface.co/SG161222/Realistic_Vision_V5.1_noVAE/resolve/main/Realistic_Vision_V5.1.safetensors"
path: "checkpoints/Realistic_Vision_V5.1.safetensors"
min_size: 2000000000
- name: "DreamShaper 8"
url: "https://huggingface.co/Lykon/DreamShaper/resolve/main/DreamShaper_8_pruned.safetensors"
path: "checkpoints/DreamShaper_8_pruned.safetensors"
min_size: 2000000000
- name: "Realistic VAE"
url: "https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors"
path: "vae/vae-ft-mse-840000-ema-pruned.safetensors"
min_size: 300000000
photorealistic:
- name: "Juggernaut XL"
url: "https://huggingface.co/RunDiffusion/Juggernaut-XL-v9/resolve/main/Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors"
path: "checkpoints/Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors"
min_size: 6000000000
- name: "RealVisXL V4.0"
url: "https://huggingface.co/SG161222/RealVisXL_V4.0/resolve/main/RealVisXL_V4.0.safetensors"
path: "checkpoints/RealVisXL_V4.0.safetensors"
min_size: 6000000000
artistic:
- name: "Deliberate v2"
url: "https://huggingface.co/XpucT/Deliberate/resolve/main/Deliberate_v2.safetensors"
path: "checkpoints/Deliberate_v2.safetensors"
min_size: 2000000000

View File

@ -0,0 +1,53 @@
# ROCm-compatible requirements.txt for ComfyUI
# Generated on Fri Jun 27 23:19:36 EDT 2025
# Base image: rocm/pytorch:latest
# Each package tested in fresh ROCm container with 300s timeout
#
# Version Information:
# - Python: 3.12.10
# - PyTorch: 2.6.0+git684f6f2
# - ROCm: 6.4.43483-a187df25c
# - ComfyUI: e18f53c (2025-06-27)
# - ComfyUI Tag: v0.3.43
# - ComfyUI Version: 0.3.43
#
# Legend:
# ✅ SAFE - can be installed without breaking ROCm
# ❌ BROKEN - breaks ROCm, fails to install, or times out
# 📦 ALREADY - already installed correctly in base image
comfyui-frontend-package==1.23.4
comfyui-workflow-templates==0.1.30
comfyui-embedded-docs==0.2.3
# 📦 ALREADY: torch
torchsde
# 📦 ALREADY: torchvision
# 🌙 NIGHTLY: torchaudio (use: pip install --pre torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm6.4)
# 📦 ALREADY: numpy>=1.25.0
einops
transformers>=4.37.2
tokenizers>=0.13.3
sentencepiece
safetensors>=0.4.2
# 📦 ALREADY: aiohttp>=3.11.8
# 📦 ALREADY: yarl>=1.18.0
pyyaml
Pillow
# 📦 ALREADY: scipy
# 📦 ALREADY: tqdm
# 📦 ALREADY: psutil
alembic
SQLAlchemy
kornia>=0.7.1
spandrel
# 📦 ALREADY: soundfile
av>=14.2.0
pydantic~=2.0
pydantic-settings~=2.0
# Testing Summary:
# ✅ SAFE packages: 18
# 📦 ALREADY installed: 9
# 🌙 NIGHTLY packages: 1
# ❌ BROKEN packages: 0
# Total tested: 28

View File

@ -0,0 +1,80 @@
{
"1": {
"inputs": {
"ckpt_name": "v1-5-pruned-emaonly.safetensors"
},
"class_type": "CheckpointLoaderSimple",
"_meta": {
"title": "Load Checkpoint"
}
},
"2": {
"inputs": {
"text": "a beautiful landscape with mountains and a lake, highly detailed, 8k, photorealistic",
"clip": ["1", 1]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"3": {
"inputs": {
"text": "blurry, low quality, distorted, deformed, ugly, bad anatomy",
"clip": ["1", 1]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Negative)"
}
},
"4": {
"inputs": {
"width": 512,
"height": 512,
"batch_size": 1
},
"class_type": "EmptyLatentImage",
"_meta": {
"title": "Empty Latent Image"
}
},
"5": {
"inputs": {
"seed": 123456,
"steps": 20,
"cfg": 7,
"sampler_name": "euler_ancestral",
"scheduler": "normal",
"denoise": 1,
"model": ["1", 0],
"positive": ["2", 0],
"negative": ["3", 0],
"latent_image": ["4", 0]
},
"class_type": "KSampler",
"_meta": {
"title": "KSampler"
}
},
"6": {
"inputs": {
"samples": ["5", 0],
"vae": ["1", 2]
},
"class_type": "VAEDecode",
"_meta": {
"title": "VAE Decode"
}
},
"7": {
"inputs": {
"filename_prefix": "ComfyUI_AMD_test",
"images": ["6", 0]
},
"class_type": "SaveImage",
"_meta": {
"title": "Save Image"
}
}
}

15
docker/startup.sh Normal file
View File

@ -0,0 +1,15 @@
#!/bin/bash
# ComfyUI Startup Script
set -e
log() {
echo "[ComfyUI] $1"
}
# Download models using Python script
python -u /workspace/download_models.py
# Start ComfyUI
log "Starting ComfyUI on port 8188..."
exec python main.py --listen 0.0.0.0 --port 8188 "$@"