Docs / Reference / Decision log
Reference
Decision log
Every non-obvious choice made while building this stack, with the reason and the measurement behind it.
Recorded so that a future change is an informed reversal rather than an accident.
Native virtual environment, not a container
Plain ComfyUI with SDXL-class models compiles nothing, so a container buys isolation at the cost of
debuggability. Containers stay the escape hatch for custom nodes that need nvcc.
Python 3.12, not the system 3.14 and not 3.13
The system interpreter is ahead of PyTorch’s published wheels. 3.12 over 3.13 because custom ComfyUI nodes are the least-maintained part of the ecosystem and lag the newest interpreter.
torch 2.11.0, not the newest 2.13.0
torchaudio — which ComfyUI requires — stops at 2.11.0 on every CUDA index. 2.11.0 is the newest
fully-matched torch/torchvision/torchaudio trio. Taking the newest torch would have made ComfyUI’s
own dependency list unsatisfiable.
cu130, not cu128
The driver exposes CUDA 13.3. Matching the wheel to the driver generation rather than to habit.
Verified sm_120 is in the compiled architecture list, not merely that CUDA reported available.
Weights outside the ComfyUI checkout
extra_model_paths.yaml points at a sibling directory. Reinstalling ComfyUI then costs a clone
rather than re-downloading tens of gigabytes.
klein-4B as primary, not SDXL
Reversal of the original plan, made after re-checking the landscape rather than trusting a stale mental model. A 4B Apache-2.0 model that generates and edits in ~5 GB beats SDXL on memory, licence clarity, and multi-reference consistency. SDXL stays installed for its LoRA back catalogue and for ControlNet-Union ProMax.
Three base models, not one
Because the winner differs by axis: klein for style and identity, Z-Image for ControlNet conditioning (klein has none), SDXL for the long tail of community LoRAs.
Apache-2.0 models preferred throughout
Several strong models — FLUX.2-dev, Ideogram 4.0, two of the three best pixel upscalers — are non-commercial. Anything that might end up in a shipped game was chosen for a permissive licence even where a non-commercial option scored higher.
Both fp8 and NVFP4 builds of klein
Blackwell executes 4-bit natively, so NVFP4 is a genuine hardware advantage on this specific card rather than a quality compromise. fp8 is the safer default; both are on disk so the comparison can be made rather than argued.
Evict on demand rather than relocating the residents
The alternative was moving the RAG replica and the pinned language model to another machine so this box is dedicated. Eviction was chosen because generation is bursty: claim the card, generate, release it. The scripts make it a single command in each direction.
The replication containers are never stopped
They hold no VRAM, and the primary’s replication push talks to them and the filesystem rather than to the query API. Stopping them would break replication for zero gain. Verified live: a scheduled push fired 25 seconds after an eviction, with ComfyUI holding the GPU, and completed cleanly.
Restore is verified, not assumed
comfy-down polls the health endpoint and exits non-zero if the service never answers. A restore
that silently failed would leave the corpus unqueryable with nothing to indicate it.
Exactly one custom node pack
Custom nodes run arbitrary code inside ComfyUI, so each one is a supply-chain decision rather than a
convenience. ComfyUI-PixelArt-Detector was installed (well-starred, actively maintained) because it
covers grid detection, palette quantisation, dithering and indexed-PNG export by itself. Three
alternatives that do the same job were skipped as small, low-traffic repositories adding no
capability. Its requirements.txt lists a bare torch, so its dependencies were installed
explicitly and the CUDA build re-verified afterwards.
The site shipped noindex, then went live the same day
A new public site stays out of search engines until someone has actually read it. It shipped with
three independent controls (meta tag, X-Robots-Tag header, robots.txt) and went live on
2026-08-09 once reviewed. All three had to be removed together — leaving any one in place keeps the
site unindexed while looking published, which is a confusing failure to debug later.
Measurements taken along the way
| What | Value |
|---|---|
| fp16 matmul, cold | 14.1 TFLOPS |
| fp16 matmul, warmed | 36.6 TFLOPS |
| Power draw under load | 91 W |
| SM clock under load | 1290 MHz |
| VRAM before eviction | 7359 MiB |
| VRAM after eviction | 2 MiB |
| Free VRAM seen by ComfyUI | 15.3 GiB |
| RAG API, just started | ~1.3 GB |
| RAG API, after first query | ~3.0 GB |
| SDXL + pixel LoRA, 1024², 20 steps | 8 s, peak 7.3 GB |
| klein-4B fp8, 1024², 20 steps | 16 s, peak 8.6 GB |
| Pixel pipeline output | 341×341, 20 colours (from 58,325) |
The cold-versus-warm figure is the one to remember: a 2.5× difference that comes entirely from whether you warmed up before timing.
Source: content/reference/decisions.md · maintained in the nuilab-aigaming repository.