Researched each of the seven SDXL checkpoints on Civitai and encoded the creator-recommended generation defaults per style instead of one global set. Material differences: - photo (CyberRealistic): dpmpp_2m_sde / karras / CFG 4 / 28 steps / CLIP 1 - juggernaut: dpmpp_2m_sde / karras / CFG 4.5 / 35 steps / CLIP 1 - pony: euler_a / normal / CFG 7.5 / 25 steps / CLIP 2 - general (Talmendo): dpmpp_2m / karras / CFG 8 / 30 steps / CLIP 2 - furry-nai (Reed): euler_a / normal / CFG 5 / 30 steps / CLIP 2 - furry-noob (IndigoVoid): euler_a-only / normal / CFG 4.5 / 20 / CLIP 2 - furry-il (NovaFurry): euler_a / normal / CFG 4 / 30 steps / CLIP 2 Three prompt-prefix dialects auto-prepended (NEVER cross-contaminated): photoreal models get nothing, Pony gets the full score_9..score_4_up chain (mandatory), and the NoobAI/Illustrious furry models get their booru quality + year-tag prefixes (masterpiece/best quality/absurdres/newest/etc). Workflow now includes a CLIPSetLastLayer node so per-style CLIP skip works. Routing default for generic "furry" flipped from Reed (NAI) to NovaFurry (Illustrious) — current sweet-spot consensus. Removed global DEFAULT_STEPS/DEFAULT_CFG valves; per-style values are canonical. Sources: each model's Civitai page (CyberRealisticXL, Juggernaut, Pony V6 XL, TalmendoXL, Reed FurryMix, IndigoVoid FurryFused, NovaFurryXL) and Pony/Illustrious prompting guides. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ai-stack — deployment
The full multi-service stack: Caddy (TLS + reverse proxy) in front of Open WebUI (chat + image generation panel), Ollama (LLMs), and ComfyUI (image generation), with an optional Anubis PoW anti-bot sidecar. One GPU host, one bridge network, one TLS entry point.
This is the only supported deployment shape — sanitized snapshot of the
production srvno.de deployment.
Files
| File | Purpose |
|---|---|
docker-compose.yml |
Service definitions, volumes, GPU reservations |
Caddyfile |
TLS + reverse proxy config (one site block per hostname) |
init-models.sh |
LLMs to preseed into Ollama on first boot |
comfyui-init-models.sh |
Checkpoints/VAEs/LoRAs to preseed into ComfyUI on first boot |
openwebui-tools/smart_image_gen.py |
Tool that auto-routes image generation to the right SDXL checkpoint |
.env.example |
Secrets and image-tag pins. Copy to .env |
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
installed and the Docker runtime configured (
nvidia-ctk runtime configure --runtime=docker && systemctl restart docker). - DNS for the chat / ComfyUI hostnames already pointing at this host (Caddy needs working DNS to provision Let's Encrypt certs on first boot).
Confirm GPU passthrough works before bringing the stack up:
docker run --rm --gpus all nvidia/cuda:12.6.3-base-ubuntu24.04 nvidia-smi
2. Configure
cp .env.example .env
# generate the two keys with: openssl rand -hex 32
Then edit:
-
.env— fill in:WEBUI_URL(full URL with scheme) andLLM_URL(bare hostname). Both point at the same Open WebUI host; Open WebUI wants the URL form for auth redirects, Anubis wants the bare hostname for its cookie domain.WEBUI_SECRET_KEYand (if using Anubis)ANUBIS_OWUI_KEY—openssl rand -hex 32for each.- Optionally pin
COMFYUI_IMAGE_TAGto a specificv*release.
-
Caddyfile— replace thechat.example.comandcomfyui.example.comhostnames with yours; replaceREPLACE_WITH_BCRYPT_HASHwith a real bcrypt hash:docker run --rm caddy:latest caddy hash-password --plaintext 'your-password' -
init-models.sh— keep the LLMs you want preseeded, drop the rest. Check sizes at https://ollama.com/library first; the host needs disk for everything listed. -
comfyui-init-models.sh— checkpoints/VAEs/LoRAs to preseed into ComfyUI. Ships empty (no active fetches) — uncomment the SDXL/Flux/ upscaler examples or add your own. Whatever filename you pick should match theckpt_namefield inworkflows/*.json(default expectsCyberRealisticXLPlay_V8.0_FP16.safetensors). SetHF_TOKENin.envif any are gated repos.
3. Bring it up
docker compose up -d
docker compose logs -f
First boot: Caddy provisions Let's Encrypt certs, the model-init
container pulls the LLMs in init-models.sh (slow — mistral-nemo:12b
alone is ~7 GB), and ComfyUI initialises empty volumes.
Health-check:
docker compose exec comfyui curl -sf http://127.0.0.1:8188/system_stats | head -c 200
docker compose exec open-webui curl -sf http://127.0.0.1:8080/health
4. ComfyUI checkpoints
ComfyUI ships no models. Three ways to get one in:
-
Preseed via the sidecar (default).
comfyui-model-initruns once oncompose up, downloads everythingcomfyui-init-models.shlists, and exits. The script ships empty — uncomment one of the examples or add your ownfetchcalls (SDXL, Flux, LoRAs, upscalers, etc.). At least one checkpoint should be namedCyberRealisticXLPlay_V8.0_FP16.safetensorsto match the workflow default, or updateckpt_nameinworkflows/*.jsonto whatever you pull. Re-run withdocker compose up -d comfyui-model-initafter script edits; already-present files are skipped. -
ComfyUI-Manager UI. Open
https://comfyui.example.com(after basic-auth login), click Manager, then Model Manager, install from the catalogue. -
Direct copy into the volume. Useful if you already have the file locally:
docker run --rm -v ai-stack_comfyui-models:/models -v $PWD:/src alpine \ cp /src/your-model.safetensors /models/checkpoints/
5. First-user signup in Open WebUI
Open https://chat.example.com. 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 for txt2img, once for img2img.
In Open WebUI: Admin Panel -> Settings -> Images.
- Image Generation Engine ->
ComfyUI(preselected via env var). - ComfyUI Base URL ->
http://comfyui:8188(preselected). - ComfyUI Workflow -> paste the entire contents of
../../workflows/txt2img.json. - ComfyUI Workflow Nodes -> paste the contents of
../../workflows/txt2img.nodes.json. - Default Model -> the filename of the checkpoint you dropped in
step 4 (e.g.
CyberRealisticXLPlay_V8.0_FP16.safetensors). - Save.
For image editing (img2img), scroll to the Image Editing section in
the same panel and repeat with
../../workflows/img2img.json and
../../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.
8. (Optional) Install the smart-routing Tool
The image-button path always uses the admin's Default Model. To get
per-prompt checkpoint routing — e.g. "draw me a cyberpunk city" picks
CyberRealistic, "anthro fox warrior" picks one of the furry checkpoints —
install the smart_image_gen.py Tool. The LLM calls it instead of the
built-in image action and chooses the right SDXL checkpoint per request.
- Workspace -> Tools -> + (top-right).
- Paste the contents of
openwebui-tools/smart_image_gen.py. - Save. Optionally adjust the Valves (ComfyUI URL, default steps, CFG, timeout) via the gear icon.
- Workspace -> Models (or pick an existing chat model) -> edit ->
under Tools, enable
smart_image_gen-> save. - Make sure the model has native function calling enabled (Workspace -> Models -> the model -> Advanced Params -> Function Calling: Native). Mistral, Qwen, and Llama 3.1+ all support this.
In a chat with that model, ask for an image — "make me a photoreal
portrait of a cyberpunk samurai" — the LLM should call
generate_image(prompt=..., style="photo"). The status bar shows
"Routing to photo (CyberRealisticXLPlay…)" while it generates.
To extend (new checkpoint, new style):
- Add the filename to
comfyui-init-models.shso it gets pulled. - Add a key to the
CHECKPOINTSdict insmart_image_gen.py. - Optionally add style-specific negatives to
NEGATIVES. - Optionally add keyword routing rules to
ROUTING_RULESfor the auto-detect path. - Re-paste the Tool source in Workspace -> Tools.
Enabling Anubis (later)
The anubis-owui service is defined in compose but no Caddy site block
points at it yet. To activate:
- Generate a key:
openssl rand -hex 32and setANUBIS_OWUI_KEYin.env. - In
Caddyfile, changereverse_proxy open-webui:8080toreverse_proxy anubis-owui:8923for the chat hostname. docker compose up -d.
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:
{ "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 that Default Model matches the filename exactly (including extension). - Out-of-memory on first generate. Lower
IMAGE_SIZEin compose (e.g.768x768) or pass--lowvram/--medvramin the Dockerfile CMD and rebuild. - Custom nodes need extra pip packages. Install via ComfyUI-Manager
(it pip-installs into the container's venv). The
custom_nodesvolume persists, but/opt/venvdoes not — so packages installed by the manager survive container restarts only because the manager re-installs them on boot. For permanent custom-node deps, add aRUN pip install …to the Dockerfile and rebuild. - GPU not visible inside container. Re-run the
nvidia-smitest in step 1. If it fails, the toolkit is misconfigured. - Caddy can't get a cert. First-boot ACME requires DNS A/AAAA
records pointing at this host's public IP and ports 80+443 reachable
from the internet. Check
docker compose logs caddyfor the specific challenge failure.