Nodes/ZenTrainer/Training Session (ZenTrainer)
ComfyUI Node

Training Session (ZenTrainer)

Your whole LoRA run hides behind one text field

By Nynxz·Created a day ago·Updated about 21 hours ago· 2
Training Session (ZenTrainer)
      ◄run_id—►

      You searched for a training node, added it to the canvas, and got a node with one text box, no outputs, and no settings at all. That's not a broken install. The run isn't in the graph, and this node was never meant to be configured by hand.

      What this node is

      ZenTrainSession is the seat a training run sits in while it uses your GPU. Its whole job, in the author's own docstring, is to claim ComfyUI's execution lane for as long as the harness trains, and to make ComfyUI's cancel button work. Everything that defines the run - model, dataset folder, rank, optimizer, learning rate, sample prompts - lives in a spec on disk, not in node inputs.

      Which decides where you set things up: you don't. Open http://127.0.0.1:8188/zentrainer/, use the New run page, and the app writes the spec and submits this node for you.

      How it works

      ZenTrainer treats ComfyUI as a library rather than a workflow engine. When you hit start, the run goes into ZenTrainer's own persistent queue, and only when nothing else is training does a worker hand the head of that queue to ComfyUI as a one-node prompt: ZenTrainSession with a run_id. That prompt exists purely to hold the execution lane, which is also why your normal generations wait behind a training run instead of landing on the GPU mid-step.

      Inside execute, the node re-reads the stored spec, builds a Trainer, and runs the epoch loop in-process. Commands - pause, resume, stop, save now, sample now, change the learning rate - go into a small queue that the loop drains between optimizer steps, so they land at clean step boundaries. Every checkpoint writes a .state.pt next to the LoRA holding optimizer state and RNG, which is what makes resume-from-crash actually work. Progress events go out over ComfyUI's websocket.

      One detail worth knowing if you've written nodes: the class returns time.time() from fingerprint_inputs, so ComfyUI can never serve this session from cache. It's the modern equivalent of the IS_CHANGED → NaN trick the plumbing layer runs on - an always-dirty node, on purpose.

      The only input

      run_id - "A run created through the ZenTrainer app or API." That's the whole surface.

      There are no outputs, so there is nothing to wire into a sampler or a save node. Trained LoRAs go to ComfyUI/output/zentrainer/loras/, which the pack registers as a loras folder, so LoraLoader can see a checkpoint the moment it's written. The finished adapter doesn't travel down a wire; it just appears in the loader's dropdown.

      Installing it

      From ComfyUI Manager, search ZenTrainer and install; or:

      cd ComfyUI/custom_nodes
      git clone https://github.com/Nynxz/ZenTrainer
      

      Then restart ComfyUI and click the lotus in the top bar, or open /zentrainer/ on your port. You need ComfyUI 0.41 or newer - the pack uses the comfy_api.latest authoring style, and it will tell you in the log if your install is too old.

      There's one gotcha that catches source installers: the built web app is not committed (web/dist/ is gitignored). A Manager or Registry install ships it; a plain git clone does not, and you'll need to build it once:

      cd ComfyUI/custom_nodes/ZenTrainer/web
      pnpm install && pnpm build
      

      Nothing else to pip install. requirements.txt is a two-line comment saying everything ships with ComfyUI. The optional optimizers (bitsandbytes, Prodigy, schedule-free, CAME, and InsightFace for the face/likeness reports) are in requirements-optional.txt - install those only if you want those optimizers listed.

      Model files come through the app's Models page, which knows the base build, text encoder and VAE for each family and pulls them off Comfy-Org's Hugging Face repos. Nothing gets re-downloaded if you already have it; the harness reads your models in place via extra_model_paths.yaml.

      Where it goes wrong

      /zentrainer/ 404s but the node is in the list. That's the missing pnpm build above. The Python side loaded fine, so you see the node and no app.

      "ComfyUI X is older than ZenTrainer needs (0.41.0 or newer)." Straight from the version check in routes.py. Update ComfyUI and restart.

      A stale run_id. If you paste a run id whose folder no longer exists under ComfyUI/user/zentrainer/runs/, the node throws on the missing spec.json. Run ids only mean something on the machine that made them, and only until you delete the run.

      Attention backends. Flash and sage attention have no backward pass, so a training run pins a differentiable backend itself. Launching ComfyUI with --use-sage-attention won't corrupt a run, and it won't make it faster either.

      If nothing trains at all: quantized bases (int8/fp8/nvfp4) deliberately run their quantized matmul in bypass mode while training - slow, but honest. Turn on gradient checkpointing before you turn the resolution down. And the thing that actually decides quality is upstream of every knob here: dataset curation, buckets, and caching text embeddings instead of re-encoding them every run.

      Categoryzentrainer

      Inputs (1)

      NameTypeDefaultDescription
      run_idSTRINGA run created through the ZenTrainer app or API.

      Outputs (0)

      No outputs