← ExpSoft OneTrainer

Wrapping OneTrainer cleanly on Windows — the AGPL-respectful EasyInstaller approach

ExpSoft OneTrainer EasyInstaller is a personal-use Windows wrapper around OneTrainer — the open-source diffusion-model fine-tuning toolkit by Nerogar, distributed under AGPL-3.0 (the legal frame is therefore personal experimentation around an AGPL-3.0 toolkit; see the Legal & Compliance section of the product page). The intent of the EasyInstaller is not to fork OneTrainer or to wrap it as a derivative — it’s a separate, independent program that installs the upstream source, at a commit validated by ExpSoft, into a sub-folder and launches it as a sub-process. The friction it removes lives entirely on the Windows side: a portable Python that OneTrainer accepts (3.12, managed by uv), conflicting dependency resolution between torch/torchvision/transformers/diffusers/customtkinter, the right PyTorch build for the card and its driver (CUDA 13.0, or CUDA 12.6 on older drivers with the 40xx package), a Windows-specific issue with the triton-windows PyPI package that can stop training mid-run, and ten ready-made LoRA profiles — Flux.2 Klein 9B and 4B, Krea 2 and Ideogram 4 — each built on OneTrainer’s own preset for its model. None of this changes OneTrainer — it just lowers the activation energy from “four hours and you give up” to “fifteen minutes and you’re training.”

· Updated · 11 min read · By Nicolas Riquier

What the EasyInstaller is — and what it deliberately is not

OneTrainer is a remarkable piece of open-source work. It supports a stack of architectures broader than any single project I know of in this space — Stable Diffusion 1.5/2.x, SDXL, SD 3 / 3.5, Flux.1 / Flux.2 (both Klein 4B and 9B), PixArt, Sana, HiDream, Chroma, Hunyuan Video, Wuerstchen, Qwen-Image, Z-Image, Stable Cascade — and lets you train LoRAs, full fine-tunes, embeddings, or run Dreambooth-style pipelines, plus captioning, plus model conversion. It is free, open-source under AGPL-3.0, and Nerogar maintains it actively. Every piece of credit for the training capabilities belongs to Nerogar and the OneTrainer contributors.

What the EasyInstaller does, very explicitly, is sit beside OneTrainer rather than around it:

This is the right legal posture for an AGPL-3.0 dependency. The EasyInstaller is a separate work in a different language, distributed as an independent program; OneTrainer is installed at the user’s request from its canonical source. No derivative-work confusion.

Why uv and a standalone Python instead of Miniconda

Up to version 1.1.0.5 the EasyInstaller installed Miniconda with Python 3.10. Version 1.2.0.0 replaces it with uv: Setup downloads the uv release as a zip, extracts it into runtime/uv/, and has it install a standalone CPython 3.12 into runtime/python/. No installer program runs, and --no-bin keeps uv from placing a python3.x.exe shortcut in the user profile, so nothing lands outside the app folder.

The uv version and the Python version are pinned together (uv 0.11.8, CPython 3.12.13). Each uv release ships its own CPython build, and Windows Smart App Control judges every binary on its own; this pair is the one already accepted on the ExpSoft reference PC. Python 3.12 sits inside the range the pinned OneTrainer commit supports (3.10 to below 3.14).

uv then creates the environment (uv venv --relocatable) and installs the requirements. Two adaptations were needed. uv refuses editable Git requirements, so OneTrainer’s -e git+…#egg=name lines become ordinary Git requirements pinned to the same commit. The Git-hosted packages (diffusers, mgds, Muon) are built without build isolation, because an isolated build runs a throw-away python.exe from the cache, which Smart App Control blocked on the test PC. Last, on the CUDA 13.0 install, helpers/constraints.txt — the exact versions of the 182 packages of the validated install — is applied, so a fresh install gets the same set.

The triton-windows story — patched out of requirements-cuda.txt

OneTrainer’s default requirements-cuda.txt includes triton-windows, an experimental Windows port of the Triton kernel compiler. On Linux, Triton is the right tool — it’s what makes torch.compile() fast on attention kernels. On Windows, the port has open issues — community reports point to “CUDA invalid memory access” crashes during long training runs on the Klein and Flux families, with the failure landing mid-training rather than at startup. Nobody has been able to reproduce them deterministically enough to fix at the source, and the maintainers have been transparent about it.

The EasyInstaller takes a deliberately conservative approach: after checking out OneTrainer (Setup Step 4, and again before the dependencies are installed), a PatchRequirements() pass rewrites the requirement files:

The ten ExpSoft profiles (more on those below) all have compile = false set explicitly, so even if Triton ever sneaks back in via a transitive dependency, OneTrainer won’t try to use it. The cost of running OneTrainer without Triton on Windows is a ~10-15% slowdown on the forward pass of large attention blocks. The benefit is that training runs of several hours don’t halt halfway through. For personal-use training on a single workstation, that trade is worth taking until upstream Triton on Windows stabilises. Upstream now pins triton-windows itself and turns compilation on in its Krea 2 and Ideogram 4 presets, and Triton compiles fresh launcher DLLs on the user’s PC at run time. The Run tab therefore writes a note in its log at each launch: with a non-ExpSoft preset, a TritonMissing error means turning off Compile transformer blocks in OneTrainer’s Model tab.

Two GPU packages — one C# source, the CUDA build chosen at Setup

Version 1.2.0.0 ships in two packages, each a separate ZIP:

The CPU-only package of earlier versions is discontinued.

Both install the PyTorch build the pinned OneTrainer commit asks for: PyTorch 2.12 with CUDA 13.0. CUDA 13 needs a recent NVIDIA driver, so the GPU detection step reads the CUDA version the driver reports through nvidia-smi. On the 40xx package, a driver below CUDA 13 switches Setup to upstream’s CUDA 12.6 requirement file. The 50xx package never takes that fallback — CUDA 12.6 has no Blackwell kernels — and asks for a driver update instead.

The split lives in the C# csproj as separate configurations: Release-50xx defines GPU_50XX, and an #if-gated TargetSeries constant in SetupService.cs tells Setup which package it is. The GPU detection step also compares the card with the package and warns on a mismatch. RTX 50 cards are recognised by their model number or a “Blackwell” name, so an RTX 3050 or 4050 is not mistaken for one.

The executable itself is a framework-dependent single file for .NET 8 (about 1.8 MB): it needs the .NET 8 Desktop Runtime, which Windows offers to download the first time the app starts.

Profile regeneration on every launch — inside OneTrainer’s preset convention

OneTrainer keeps its presets in training_presets/, one folder per model family, and its Qt interface lists only files whose name starts with # — Nerogar’s built-in presets, such as Flux 2/#flux2 LoRA 16GB.json. That is why the 1.1 profiles (ExpSoft_*.json at the root of the folder) no longer appeared once OneTrainer moved to its Qt UI. Version 1.2.0.0 writes its ten profiles as training_presets/ExpSoft/#ExpSoft <name>.json, so they show up as a group of their own, Load Preset › ExpSoft:

ProfileModelUpstream presetResolutionBatchOffload
#ExpSoft Klein 9B LoRA 16GBKlein 9BFlux 2 · 16GB5122preset
#ExpSoft Klein 9B LoRA 8GBKlein 9BFlux 2 · 8GB51210.7 transformer + 0.7 text encoder
#ExpSoft Klein 9B LoRA 1024Klein 9BFlux 2 · 16GB102410.5 transformer
#ExpSoft Klein 4B LoRA 16GBKlein 4BFlux 2 · 16GB5124preset
#ExpSoft Klein 4B LoRA 8GBKlein 4BFlux 2 · 8GB51220.5 transformer + 0.5 text encoder
#ExpSoft Klein 4B LoRA 1024Klein 4BFlux 2 · 16GB102420.3 transformer
#ExpSoft Klein 4B LoRA 1536Klein 4BFlux 2 · 16GB153610.6 transformer
#ExpSoft Krea 2 LoRA 16GBKrea 2 RawKrea 2 · 16GB51220.3 (preset)
#ExpSoft Krea 2 LoRA 1024Krea 2 RawKrea 2 · 16GB102410.9 transformer
#ExpSoft Ideogram 4 LoRA 512Ideogram 4Ideogram 4 · 16GB51220.5 transformer

Each profile is OneTrainer’s own preset for its model, read from the pinned checkout, with ExpSoft overrides on top: the model (the local folder when it was downloaded through the Settings tab, otherwise the Hugging Face repository id), resolution, batch size, per-component offload with activation offloading and gradient checkpointing, compile = false (the Triton-out posture from the previous section) and the absolute paths of the install. Learning rate, rank, precision and quantisation stay those of the upstream preset, with one exception. On Ideogram 4, the upstream strength stayed stable but did not learn the test subject in 600 steps, so the ExpSoft profile uses a learning rate of 1e-4 with rank 64 / alpha 64 over 60 epochs, and leaves out Ideogram’s unconditional transformer, which only serves the negative branch of the sample previews. Offload keeps a fraction of a component’s layers in system RAM: on the 16 GB test card, Krea 2 at 1024 px with the preset’s 0.3 offload spilled into shared memory (42 s per step), while 0.9 plus activation offloading ran at about 5 s per step.

The non-obvious decision is that these ten profiles are regenerated from scratch on every launch of OneTrainer from the Run tab, rather than being saved once at Setup time. Three reasons:

  1. Drive-letter changes. If the install folder is on F: today and G: tomorrow (USB-stick scenario, or a drive reorganisation), the absolute paths embedded in the profile JSON (workspace, output, samples, concepts, latent cache) need to follow the move. Regenerating beats string-replacing.
  2. OneTrainer schema migrations. OneTrainer loads its built-in presets without config migration, so they must already use the current schema — since config version 11, offload and gradient checkpointing are set per component. Building every ExpSoft profile from the upstream preset of the pinned checkout keeps it on the schema that OneTrainer version expects. No “schema version mismatch” errors at load time.
  3. Strict scope. ProfileService writes only #ExpSoft *.json files inside training_presets/ExpSoft/, and removes those a version no longer ships (plus, once, the root-level ExpSoft_*.json files of 1.1). Nerogar’s presets are read but never written. concepts.json is created once and never rewritten, so the user’s concepts stay theirs. Edits made directly to an ExpSoft profile are overwritten at the next launch; OneTrainer’s Save config keeps them in a file of their own.

Portable venv — relocatable from the start, patched on every launch

Setup creates the environment with uv venv --relocatable. A few files still record absolute paths: pyvenv.cfg (its home line points at runtime/python/cpython-…), the text files of Scripts/, every .pth and .egg-link file in site-packages/, and every direct_url.json in .dist-info/ folders. If the install folder moves (different drive letter, different parent path), those need to follow.

PathService.AutoPatchVenvPaths() runs at every launch from the Run tab. It reads the home line of pyvenv.cfg, derives the app root the venv was created under, compares it to the current AppContext.BaseDirectory, and if they differ replaces the old root with the new one in those files, in both backslash and forward-slash form, skipping .exe, .dll and .pyd binaries. The patch is idempotent — running it on an already-correct venv is a no-op.

End result: move the install folder to another drive, or copy it to another Windows PC with the .NET 8 Desktop Runtime installed, double-click the EasyInstaller exe, and OneTrainer launches. No reinstall.

The Samples tab — live previews from the training run

OneTrainer’s training loop periodically generates sample images at user-defined intervals, dropping them into a samples/<n - prompt>/ folder of the training’s workspace. Each ExpSoft profile has its own workspace (workspace/<profile>/), so every training gets its own samples folder. By default they’re invisible unless you go looking. The EasyInstaller adds a sixth tab — Samples — that watches the whole workspace/ folder via a FileSystemWatcher with IncludeSubdirectories, keeps every image (PNG, JPEG, BMP, WebP) found under a samples folder, labels it with its training and prompt, and presents new samples in a WrapPanel gallery as they arrive, newest first.

A few small implementation details that pay back:

RAM watchdog — protecting the host from Klein 9B

Klein 9B training, especially with CPU offload at 0.7, can consume 18 GB or more of system RAM (offloaded layers + optimiser state + gradients + dataset latent cache). On a 32 GB machine that leaves comfortable head-room; on a 16 GB machine it swaps and the entire system grinds to a halt.

The EasyInstaller runs a small background task during training that calls Win32 GlobalMemoryStatusEx every three seconds. If available physical RAM drops below 2 GB, the watchdog logs a warning and stops OneTrainer, ending its whole process tree. A deliberate stop beats the alternative — Windows’ own OOM-killer taking out random processes, including potentially the IDE the user has unsaved work in.

Credits and license

The work that makes this useful belongs to upstream:

Diffusion model weights (Flux, SDXL, Klein, etc.) each have their own license — Klein 9B and 4B are gated on HuggingFace and require accepting the Black Forest Labs terms individually; SDXL is OpenRAIL++; PixArt is Apache 2.0; and so on. The EasyInstaller doesn’t bundle any model weights — the user downloads them with their own HF token through the Settings tab.

Take-aways

Frequently Asked Questions

Quick answers to what people ask AIs about this article specifically.

Is ExpSoft OneTrainer a fork of OneTrainer?

No. ExpSoft OneTrainer EasyInstaller is an independent C# / WPF program that fetches the upstream OneTrainer source from Nerogar’s GitHub at Setup time — at a commit validated by ExpSoft — into a project/ sub-folder, then launches OneTrainer’s own Python GUI as a sub-process at Run time. The OneTrainer source is intact apart from the requirement files Setup adjusts, AGPL-3.0 preserved, ready to be inspected. The EasyInstaller is a launcher and a configurator, not a derivative work.

Why is the triton-windows package removed during Setup?

The Windows port of Triton has open issues that can manifest as crashes during long training runs on the Klein and Flux model families — the failure lands mid-training rather than at startup, which makes it especially costly. Until those issues stabilise upstream, the EasyInstaller removes triton-windows from OneTrainer’s requirements-cuda.txt at Setup, and forces compile = false in the ten ExpSoft profiles. The cost is a ~10-15% slowdown on the forward pass of large attention blocks. The benefit is training runs of several hours that don’t halt halfway through.

Why separate 40xx and 50xx packages, and what happened to the CPU build?

Both packages install PyTorch 2.12 with CUDA 13.0, the build the pinned OneTrainer commit asks for. They differ in what Setup does with an older driver. The 40xx package (RTX 20xx/30xx/40xx) falls back to upstream’s CUDA 12.6 requirement file when the driver reports less than CUDA 13. The 50xx package never does — CUDA 12.6 has no Blackwell kernels — and asks for a driver update instead. At Setup the GPU detection step also warns if the package does not match the card. The CPU-only package was discontinued in 1.2.0.0.

What does the EasyInstaller change in the user’s system?

Nothing outside the install folder. No PATH mutation, no %USERPROFILE% caches or shortcuts, no registry writes. uv and its Python are installed under runtime/uv/ and runtime/python/, the venv lives under project/venv/, all caches (HF, PyTorch, pip, uv) are redirected under cache/. The only system prerequisite is the .NET 8 Desktop Runtime. Uninstall = delete the folder.

Can the install be moved to a different drive or USB stick after Setup?

Yes. At every launch, PathService.AutoPatchVenvPaths() reads the venv’s recorded root path from pyvenv.cfg, compares it to the current install location, and if they differ rewrites all the absolute paths in the venv text files. ProfileService.PatchExpSoftProfiles() regenerates the ten ExpSoft profiles with the new paths. Move the folder to another drive, or to another Windows PC with the .NET 8 Desktop Runtime, double-click — OneTrainer launches without reinstall.

Want to use ExpSoft OneTrainer EasyInstaller?

Get it on Patreon →