install()¶
# install.py
from comfy_env import install; install()
The build-time entry point, called once when a pack is installed or updated.
In the standard install path, ComfyUI-Manager pip-installs the pack's
requirements.txt and then executes its install.py. A user cloning by hand
should do the same.
Of the three calls, this is the only one
that does network and disk work. install() is the sole builder of isolated
envs: nothing materializes one at runtime. A missing env means
register_nodes() falls back to in-process import for that
pack, and stays that way until install() is run successfully.
If an install has already failed and you are here to find out why, skip to When it fails.
What install() does¶
Two things happen, in order:
(1) peer packs named in [node_packs] are
installed, if the config declares any;
(2) every isolated env declared anywhere under
custom_nodes/ is built or refreshed. Only (2) is slow, but if the isolated envs had already been built it exits without touching the network.
1. Peer packs from [node_packs]¶
Runs only if the config declares [node_packs]; every accepted spelling is
tabulated in the config reference.
Peer nodepacks are cloned from GitHub or downloaded from the Comfy Registry, then
their own install.py runs.
Their requirements.txt is not installed. A peer is required to be
comfy-envved (ADR-0016), so its
dependencies belong in its own isolated env, and its requirements.txt should
name nothing but comfy-env. comfy-env will not pip-install on another pack's
behalf into the environment it exists to keep clean. A peer that is not
comfy-envved is cloned but will not load, which is the visible failure rather
than the silent one.
The peer's install.py still runs, and nothing stops it pip-installing into
the host env from there. That route is open and tracked as a
direction to close.
2. The workspace build (install_workspace())¶
Runs only if the ComfyUI base directory can be located; if it cannot,
install() warns and skips the workspace entirely, leaving no envs built.
Where the envs land¶
The workspace is machine-wide, not per install, so two ComfyUI installs using the same pack share one materialized env (ADR-0007).
| Workspace root | |
|---|---|
| Windows | %LOCALAPPDATA%\Programs\comfy-env |
| Linux / macOS | ~/.ce |
COMFY_ENV_ROOT moves it. On Windows the root is deliberately not at a
drive root, as something like C:\ce would need admin to create.
One directory per env, under envs/:
<root>/envs/<env-name>_<abi-tag>/ the manifest (pixi.toml, pixi.lock)
<root>/envs/<env-name>_<abi-tag>/.pixi/envs/default/ the materialized env
The seam is an underscore, and it is the only one in the name (ADR-0039). Split on it and you have the two halves; nothing else in either half can be an underscore.
<env-name>is the pack directory,ComfyUI-/ComfyUI_prefix stripped and lowercased, plus-<subdir>when the config is not at the pack root. Anything outside[a-z0-9-]collapses to a single dash, because this half comes from a folder name on disk and pixi rejects the rest.<abi-tag>ispy<version>-torch<major>.<minor>-<backend>, where backend iscu128,rocm63,mps,cpu, ornotorch. Version dots are kept, so torch 2.10 readstorch2.10and cannot be misread as torch 2 build 10.
The tag is what stops two ComfyUI installs on different stacks from sharing a directory and rebuilding over each other. It also means the same pack can hold several copies at once, one per stack it has been installed under:
geometrypack-nodes_py310-torch2.10-cpu
geometrypack-nodes_py311-torch2.10-cpu
geometrypack-nodes_py313-torch2.8-cu128
comfy-env gc is a command that can be used to delete envs under <root>/envs/ that no
installed pack references (ADR-0028).
Full disk layout, including the pixi package cache that COMFY_ENV_ROOT
does not move, is in Drives and volumes.
Bootstrap and discovery¶
- We run
ensure_pixi()first - Discovery then walks
custom_nodes/for bindable configs (comfy-env.toml files). - Three things are skipped silently, and one is fatal:
- directories prefixed
.or_, and those suffixed.disabled/._disabled(the quarantine convention) -- skipped; - configs outside
nodes/comfy-env.tomlornodes/<subdir>/comfy-env.toml-- invisible, deliberately, because the runtime binder can only bind those two shapes; - a config that does not parse -- skipped, warned inline as the scan hits it, then listed again as a batch as soon as discovery finishes (before the skip gate, not at the end of the install);
- two configs deriving the same env name --
ValueError, because they would share one env directory and rebuild over each other forever (workspace.py).
- directories prefixed
The skip gate¶
Two hashes decide whether any environments are rebuilt:
- A cheap fast key over inputs (does this machine have a GPU? Is the cpu aarch64 or x86? what is comfy-env.toml saying?)
- A precise identity over what those inputs derive to. The full mechanism, including why a version bump rebuilds nothing, is The three seals.
Which combo the envs get¶
Usually the cuda-wheels index has every needed wheel for the host combo -- the (cuda × torch × python) triple ComfyUI itself runs -- and we can match it perfectly.
When any of the cuda packages is not yet built for the host combo (imagine we are using CUDA 13.0, have [cumesh, flash-attn, spconv] as cuda packages in comfy-env.toml and we only have cumesh and flash-attn for CUDA 13.0) the requested combo for the cuda wheels drops to a known-good fallback cell.
The fallback is per CPU architecture:
cu12.8 / torch 2.8on x86_64cu13.0 / torch 2.10on linux aarch64.
ARM needs its own cell because (12.8, 2.8) has no aarch64 wheels at all; the
full argument is Why ARM gets its own fallback
cell.
The CUDA wheels are inside the generated manifest, as direct-URL
pypi-dependencies: they land in pixi.lock.
No NVIDIA GPU means the CPU wheel index, whatever the host's torch says
Portable ComfyUI ships torch+cu128 inside python_embeded even on
machines with no NVIDIA driver. NVIDIA GPU presence therefore
overrides the torch build (workspace.py): with none detected,
envs resolve torch from the CPU index and [cuda] packages are not
resolved or installed at all.
This is a Linux and Windows rule and does not apply to macOS. Darwin
never reaches the CUDA-index choice at all (workspace.py); macOS
torch comes from ordinary PyPI, and those wheels have MPS compiled in
(detection/backend.py). There is no separate MPS build to pick
and nothing to opt into: a Mac with MPS available is detected as backend
mps, so its envs are tagged -mps and never share a directory with a
genuinely CPU-only machine's.
Building each env¶
The work is phase-major, not env-major: every env goes through a phase before any env goes through the next.
-
Manifests are written for each env
-
All installs run
-
All stamps are produced
-
All hash files are produced
That ordering is deliberate and produces three behaviours worth knowing:
- One
pixi installper manifest, so a broken manifest cannot poison another env's scan or install. pixifailures are collected and raised at the end (workspace.py), so one run surfaces every broken env rather than stopping at the first.- Hash files are written last (
workspace.py), after that raise point. So if any env fails, the run leaves no hash bookkeeping for the envs that succeeded alongside it, and they are re-derived next time.
When it fails¶
Start here: a workspace install that does any work tees its full output to
<workspace>/install.log (workspace.py) -- the discovery list, the
resolved combo, and each pixi install invocation with its output.
The log is from the last run that did work
A run where every env is already current returns in workspace.py,
before the log is opened at. So after a clean run the file on
disk is an older transcript, and its timestamp is the only tell. To force
a fresh one, delete an env's install.hash -- which is what the skip
message itself tells you to do.
Because failures are batched and raised at the end, one log names every broken env rather than stopping at the first. Re-run with:
comfy-env install --dir custom_nodes/<pack>
Two failures produce no envs and no pixi output at all: the ComfyUI base
directory could not be located (see section 2), or two configs derived the
same env name and raised a ValueError before any build started.