Configuration
mold keeps configuration in two places by design:
config.toml— the hand-editable bootstrap file at$MOLD_HOME/config.toml(default~/.mold/config.toml). Owns paths, ports, credentials, and the model-path entries thatmold pullwrites.~/.config/mold/homeis a one-line pointer file that names$MOLD_HOME; it is not a second config file.mold.db(SQLite) — owns user preferences: the[expand]section, scheduler timing preferences (scheduler.*), gallery and queue retention (gallery.trash_retention_days,queue.held_retention_days),generate.auto_tag_title, the TUI'stui.*preferences, global generation defaults (default_width,default_height,default_steps,embed_metadata,t5_variant,qwen3_variant,default_negative_prompt), thelast-modelsidecar, and per-model generation defaults (default_steps,default_guidance,default_width,default_height,default_frames,default_fps,scheduler,negative_prompt,lora,lora_scale). These fields moved to the DB in #265 so GUI writes and hand-curated TOML no longer fight over the same file.default_framesanddefault_fpsare DB-owned but written by the generating surfaces:mold config set models.<name>.<field>accepts only the eight other per-model fields, andumt5_variantis a registered key name with no read/write arm and no DB slot (#778) — useMOLD_UMT5_VARIANT.
Environment variables still override both surfaces at read time. Upgrading from an earlier release runs a one-shot import of the preference slices of config.toml into the DB on first launch — your existing values carry over.
The web app's Settings → All settings panel exposes this same effective configuration surface. It provides typed editors for known keys, keeps unknown future keys visible under Advanced, labels each value's DB/file/environment provenance, prevents writes that an environment variable would override, and supports per-key reset plus profile creation and switching.
output_dir is a startup-only trust root for a running server. The live PUT /api/config/output_dir endpoint returns 409 RESTART_REQUIRED without changing memory or disk. Stop the server, run mold config set output_dir <path>, then restart it.
Managing Config from the CLI
mold config routes writes to the right surface based on the key prefix:
mold config list # All settings tagged [db] / [file] / [env]
mold config list --json # JSON form: { "value": …, "surface": … } per key
mold config get server_port # Get a value
mold config set server_port 8080 # Bootstrap key → writes config.toml
mold config set expand.enabled true # User preference → writes mold.db
mold config set default_width 1024 # Generation default → writes mold.db
mold config set scheduler.replan_debounce_ms 2000 # Scheduler timing → mold.db
mold config set gallery.trash_retention_days 7 # Library trash retention → mold.db (0 = keep forever)
mold config where expand.enabled # Print which surface owns this key
mold config reset expand.enabled # Drop the DB row; next read falls back to TOML/env/default
mold config reset --all --yes # Drop every DB row under the active profile
mold config --profile portrait list # Scope a command to an explicit profile (v6)
mold config edit # Open config.toml in $EDITORmold config list tags every row with its surface so you can see at a glance which store owns each key — [db] for mold.db, [file] for config.toml, [env] when a MOLD_* env var is currently overriding. mold config set prints the same tag in its output (for example Set expand.enabled = true [db]). mold config where <key> also reports any env override that beats both stores at runtime.
mold config reset <key> drops the DB row so the next read falls back to the TOML/env/compiled default — useful for "undo the wrong setting" without hand-editing mold.db. TOML-only keys are rejected with a pointer at mold config set since those live in the hand-edited file. mold config reset --all purges every DB row under the active profile (prompts for confirmation unless --yes is passed).
Multi-profile (schema v6)
settings and model_prefs rows are keyed on (profile, key) / (profile, model) — one DB can host multiple independent preference sets (default, dev, portrait, …). Active profile resolves in priority order:
MOLD_PROFILEenv var,- the
profile.activesetting row under thedefaultprofile, "default".
Every mold config subcommand accepts --profile <name> to scope for a single invocation without touching the env or the meta setting.
Scheduler V2 reads its profile-scoped timings when the coordinator starts. scheduler.replan_debounce_ms defaults to 2000, scheduler.replan_max_delay_ms to 5000, and scheduler.warm_wait_max_ms to 2000. Each accepts 0–30000 milliseconds; maximum delay must be at least the debounce. The config API and Mold Studio mark these rows restart-required. Restart the server after changing them.
See the CLI Reference for the full list of keys and options.
Config File
default_model = "flux2-klein:q8"
models_dir = "~/.mold/models"
server_port = 7680
default_width = 1024
default_height = 1024
# Global default negative prompt (CFG models only)
# default_negative_prompt = "low quality, worst quality, blurry, watermark"
[models."flux-dev:bf16"]
default_steps = 25
default_guidance = 3.5
# lora = "/path/to/adapter.safetensors"
# lora_scale = 0.8
[models."sd15:fp16"]
default_steps = 25
default_guidance = 7.5
negative_prompt = "worst quality, low quality, bad anatomy"
[expand]
enabled = false
backend = "local"
model = "qwen3-expand:q8"
temperature = 0.7
# Per-family expansion tuning
# [expand.families.sd15]
# word_limit = 50
# style_notes = "Short keyword phrases for CLIP-L."
# [expand.families.flux]
# word_limit = 200
# style_notes = "Rich natural language descriptions."
[logging]
# level = "info" # Log level (overridden by MOLD_LOG env var)
# file = false # Enable file logging to ~/.mold/logs/
# dir = "~/.mold/logs" # Custom log directory
# max_days = 7 # Days to retain rotated log files
[lambda]
# api_key = "..." # Prefer LAMBDA_API_KEY for shells
# endpoint = "https://cloud.lambda.ai/api/v1"
# image_repository = "ghcr.io/utensils/mold"
# ssh_key_name = "mold-laptop"
# ssh_private_key_path = "~/.ssh/id_ed25519"
# filesystem_prefix = "mold"
# filesystem_mount_path = "/data/mold"
# confirm_hourly_usd = 5.0
# local_port = 7680Environment Variables
Environment variables take precedence over config file values.
Core
| Variable | Default | Description |
|---|---|---|
MOLD_HOME | ~/.mold | Base directory for config and cache |
MOLD_DEFAULT_MODEL | flux2-klein:q8 | Default model name |
MOLD_HOST | http://localhost:7680 | Remote server URL |
MOLD_MODELS_DIR | $MOLD_HOME/models | Model storage directory |
MOLD_ARTIFACT_ATTESTATIONS_DIR | $MOLD_HOME/.artifact-attestations-v1 | Owner-private digest receipts written at acquisition; existing models use local installation identities when receipts are unavailable. Set this to private service state when MOLD_HOME is shared or group-writable |
MOLD_PORT | 7680 | Server port |
MOLD_MDNS | 1 (on) | Set 0/false to disable mold serve LAN advertising and server-assisted DNS-SD browsing (requires the mdns build feature) |
MOLD_DISPATCH_MODE | v2 | Restart-time GPU dispatch owner: V2 is authoritative by default. During the one-release rollback window, legacy restores the prior depth-two transport and observe retains legacy ownership while recording request/placement/host-memory-feasible V2 decisions read-only. Queue pause gates generation, utility, and admin GPU work in every mode. Invalid values fail startup. |
MOLD_DISTRIBUTION_IMAGE_VERSION | latest | Release-build input: official stable builds embed their exact release, fetch that release's target digest manifest, and submit repository@sha256:…; source/Nix/rolling builds use mutable latest*. End users should not override it at runtime. |
LAMBDA_API_KEY | unset | Overrides lambda.api_key |
MOLD_LOG | info (serve) / warn (cli, tui) | Log level |
Development and qualification
| Variable | Default | Description |
|---|---|---|
MOLD_TEST_CUDA_FP8 | unset | Test-only switch that runs the Hunyuan3D 2.1 grouped-FP8 linear parity contract on CUDA instead of CPU. Production ignores it. |
MOLD_TEST_PULID_ASSETS | unset | Test-only path to the pinned PuLID and AntelopeV2 assets used by ignored parity tests. It may name the checkpoint file or a directory searched one level deep; production ignores it. |
Generation
| Variable | Default | Description |
|---|---|---|
MOLD_EAGER | — | 1 to keep all components loaded |
MOLD_OFFLOAD | — | 1 to force block offload for FLUX, Flux.2, Z-Image, Qwen-Image, LTX-2, Wan, and SD3 BF16/FP8 paths where implemented. FLUX / Flux.2 / Z-Image / Qwen-Image keep fitting blocks GPU-resident and stream only overflow blocks; LTX-2, Wan (GGUF only — it parks every block), and SD3 full-stream. |
MOLD_OFFLOAD_PREFETCH | on | FLUX offload async H2D prefetch stream — set off to revert to synchronous |
MOLD_PINNED_VRAM_MAX_GB | RAM × 0.5 | Cap on pinned host memory used by the FLUX offload path |
MOLD_RESERVE_VRAM_MB | 400 (Linux) / 600 (Windows) / 0 (macOS) | OS / cuBLAS workspace reserve subtracted from free_vram_bytes before any budget decision. Set explicitly to override the platform default; 0 disables |
MOLD_KEEP_TE_RAM | auto | Whether text encoders stay in host RAM between requests instead of being dropped. Tri-state. auto (unset) measures the host: a park is admitted only when the encoder, the transformer that loads beside it, and a max(15 % of RAM, 8 GiB) safety floor all fit in available memory — so a 64 GB desktop streams as it always did and a 1.5 TB host parks and page-locks. 1 parks whenever the encoder alone clears the floor (an operator saying they know the machine), and is also the unchanged opt-in every other family reads: FLUX/SD3's T5, Wan's UMT5. 0 never parks. FLUX.2 [dev] adds one rule on top of the budget, because its prefix is STREAMED rather than materialized and so parking it is a fresh ~35 GB read of the shards: under auto the FIRST encode of a process streams from the mapping and the park engages from the second, so a one-shot mold run never pays a read only a later render could collect. An explicit 1 overrides that too and parks from the first encode. Disabled on Metal, where the "parked" copy would sit in the pool the encoder already runs from. Quantized GGUF encoders park too — Qwen-Image's Qwen2 since #1044, and Flux.2 Klein's and Z-Image's Qwen3 now — with the QTensor bytes moving host↔device losslessly rather than being re-read from disk. FLUX.2 [dev] parks the Mistral3 prefix it streams (~35 GB at BF16), filtered to the layers it actually runs, never the whole 36 GB file. |
MOLD_LORA_BYPASS | auto | FLUX LoRA application path: auto enables bypass-mode when LoRAs are present (covers offload AND the GGUF/quantized path via quantized_transformer.rs), on always bypasses, off reverts to legacy merge-into-base / gguf_lora_var_builder |
MOLD_VAE_TILED | auto | Tiled VAE decode for FLUX/FLUX2/SDXL/SD3 and Z-Image on Metal: auto retries with tiling on OOM, force always tiles, off disables tiling. Z-Image completes Metal attempts before accepting pixels |
MOLD_STEP_PREVIEW | 1 | Live denoise previews over /api/generate/stream (preview SSE events; FLUX.1/Flux.2/Z-Image/Wan — Wan projects the clip's middle latent frame): a small latent-resolution PNG per step via linear latent→RGB projection, throttled to ~700 ms. 0 disables. |
MOLD_LONG_PROMPTS | — | 1 enables ComfyUI-style chunked CLIP encoding (75-token windows, BOS/EOS framing, pooled outputs averaged into the FLUX vector_in 768-dim conditioning). Default off — pre-Tier-2 hard truncation at 77 preserved. |
MOLD_ATTN | per family: flash for video and FLUX, math for other images | Attention backend: math (hand-rolled SDP) or flash (candle-flash-attn v2). Flash needs a --features cuda,flash-attn build and a CUDA fp16/bf16 tensor. Most shipped Linux CUDA artifacts are such a build — mold (sm89, through h3-cuda), mold-sm86, mold-sm100 and the matching desktop packages. mold-sm120 is NOT: FlashAttention selects its tile from a runtime compute-capability test that reads consumer Blackwell as a large-shared-memory datacenter part, so an RTX 50-series card would take a tile tuned for an A100, and mold has no such card to measure that on — so that artifact resolves to math until it is qualified. A self-built --features cuda binary without flash-attn, and every Metal build, resolve to math too; that is still correct but it is the slow half of 0.29's FLUX change, because FastStill's math path folds the softmax scale into K whether or not the kernel is compiled, so such a build takes the archived-seed break without the speedup. The default is per family. Video DiTs (Wan, LTX-2) default to flash wherever the kernel is compiled in, because a clip is re-rendered for its content and the math path costs 2.1x. FLUX.1 and FLUX.2 also default to flash wherever the kernel is compiled in: for those two the math score matrix is the render rather than a rounding error beside the weights (~700 GB of score-matrix traffic per step at 1024²). Every other image family defaults to math in every build, so the image an archived seed renders never depends on which artifact you installed. Setting this variable overrides all three. FLUX's dense BF16 transformer arm is the exception on the flux side — it attends through upstream Candle and is unaffected. Reaches FLUX offload/GGUF-bypass, all Flux.2, Z-Image offload, the Wan DiT's self/cross attention, and all three Qwen-Image transformers (BF16, quantized, offloaded) on CUDA/CPU — Qwen keeps candle's fused sdpa on Metal; dense BF16 FLUX and Z-Image use upstream Candle's own attention and are unaffected. On Wan, flash is a speed lever rather than a memory one: measured on an RTX 4090 (wan22-t2v-a14b:q5, 53f/832x480, same binary, only the backend varying) it renders in 75.3 s against math's 158.4 s — 2.1x — while peak VRAM falls only from 22,250 MiB to 21,354 MiB. Ineligible tensors fall back to math silently; a build without the feature warns once. |
MOLD_FLUX2_QMATMUL | 1 | Flux.2 GGUF linear arm on CUDA: 1 (default) keeps candle's quantized fast path, 0/off restores the per-forward dequantization. The default is on — unlike Qwen-Image's and Z-Image's equivalents — because FLUX has rendered correctly through those kernels for as long as it has loaded GGUFs. Set it to 0 if a Flux.2 render comes out black or blank, and say so in an issue. |
MOLD_FLUX2_FP8_CACHE | budgeted | How a Flux.2 FP8 tier holds its weights. Unset (and any value other than 0/1) measures the card before the first weight lands: if three copies of the checkpoint plus the runtime headroom fit in usable free VRAM, the F8 slab is widened to the working dtype once at load and dropped — two bytes per parameter at rest, and no per-forward cast. Otherwise the slab is kept and widened on every forward, which is the historical behaviour and the answer whenever free VRAM cannot be read at all (CPU, Metal, a build without NVML). 1 forces the widened arm, 0 forces the per-forward one. Both arms are the same arithmetic in the same order — the per-tensor scale still rides the matmul output — so this is a residency/latency choice, not a numerical one. |
MOLD_FLUX2_FP8_GEMM | off | 1 routes Flux.2 FP8 linears to the fork's native cuBLASLt (F8E4M3, F8E4M3) -> BF16 GEMM instead of widening the weight. Unqualified and off by default: it quantizes the activation, which the widen path never does, and nothing here has been compared against a ComfyUI torch._scaled_mm parity fixture. It warns once when enabled. CUDA only; ignored everywhere else. |
MOLD_FLUX_DEBUG_NONFINITE | off | 1 checks every FLUX.1 / Flux.2 denoise step for NaN or infinity and fails the render naming the step. Off by default and a single boolean when off; on, it costs one device synchronization and one reduction per step. A diagnostic, not an engine input — it cannot change a pixel. |
MOLD_ATTN_CHUNK | auto | Override math-attention query chunk size. Positive integers below the sequence length enable chunking; 0 or off disables it. The CUDA default chunks long queries at 512 to reduce peak VRAM. |
MOLD_CONV | per family: cudnn for video and FLUX, im2col for other images | Convolution backend: im2col (materialise the column buffer, then one GEMM) or cudnn (NVIDIA's kernels). cudnn needs a --features cuda,cudnn build — every Linux CUDA release artifact is one — and is chosen per convolution inside candle, which stays on im2col for 1x1 kernels and for shapes too small to repay cuDNN's per-call setup, and falls back to im2col if a cuDNN call errors. Explicit per-layer algorithms bypass the automatic size heuristic when cuDNN is enabled. The Hunyuan3D paint material upscaler scopes its own cuDNN default; shape generation retains the image default. MOLD_CONV overrides both. The default is otherwise per family, on the same argument as MOLD_ATTN: video families (Wan, LTX-2) and FLUX.1/FLUX.2 default to cudnn, every other image family to im2col in every build so an archived still's bytes never depend on which artifact you installed. FLUX's transformer has no convolutions at all, so the flux scope covers VAE encode and decode. Measured on an RTX 4090 (wan22-t2v-a14b:q5, 832x480, one steady-state latent frame of the VAE decoder): 845 ms of convolution on im2col against 192 ms on cuDNN, a 4.4x reduction that takes the decode from ~21% of the render to ~10%. The two backends sum in a different order and do not agree bit-for-bit. |
MOLD_HUNYUAN3D_DECODE_CHUNKS | 8000 (32000 on Metal) | Query points the Hunyuan3D shape VAE decodes per chunk. Metal's default is measured (M4 Max, octree 256: 32000 takes 19% off the volume decode and yields a byte-identical mesh; 100000 is slower again). The occupancy field is evaluated on (octree + 1)³ points — about 17 million at the default resolution 256 — so this is what keeps the decode bounded rather than materialising the whole grid at once. Lower it if the decode is the stage that runs out of VRAM; raising it trades memory for slightly fewer kernel launches. Clamped to 256..=1,000,000. Chunk boundaries change the order floating-point sums are accumulated in, so two renders that disagree on this value are not bit-identical and mold treats them as different execution classes. |
MOLD_EMBED_METADATA | 1 | 0 to disable PNG metadata |
MOLD_MEDIA_ROOTS | — | Platform path-list of allow roots for trusted server-local LTX-2 audio_file_path / source_video_path requests. Targets are canonicalized and must resolve to files under one configured root. |
MOLD_PREVIEW | — | 1 to display images inline in terminal |
MOLD_T5_VARIANT | auto | T5 encoder: auto/fp16/q8/q6/q5/q4/q3 |
MOLD_UMT5_VARIANT | auto | Wan UMT5 encoder: auto/fp16/q8/q6/q5. Auto prefers FP16 on GPU when it fits, else the largest GGUF that does — a CPU-parked UMT5 widens to F32 |
MOLD_QWEN3_VARIANT | auto | Qwen3 encoder: auto/bf16/q8/q6/iq4/q3 |
MOLD_SCHEDULER | — | SD1.5/SDXL: ddim / euler-ancestral / uni-pc / edm-dpm-pp-2m (Playground v2.5 only). Wan solvers are selected with --sample-solver (unipc / euler / dpm++) |
MOLD_CFG_PLUS | — | 1 to enable CFG++ (manifold-projection guidance, Chung et al. 2024). Drops usable CFG to ~1.5–2.5 and removes guidance artifacts. Per-request --cfg-plus overrides. Supported on SD3, SDXL, and SD1.5 (DDIM only — Euler-A / UniPC fall back). Ignored by FLUX / Z-Image / Flux.2 (distilled). |
MOLD_VAE_DTYPE | auto | Override VAE precision: auto, bf16, fp16, fp32. Use fp32 to fix banding artifacts on FLUX/SD3 finetuned VAEs (~2× decode VRAM; tiled VAE absorbs OOM via existing fallback). Wired into FLUX, FLUX2, SD3, SDXL, SD1.5. |
MOLD_NVFP4_BACKEND | auto | NVFP4 backend selection for Flux.2 and LTX-2: auto and portable use portable CPU BF16 streaming dequant; native is reserved for validated sm_120/Blackwell tensor-core execution and fails clearly on non-Blackwell hosts. |
MOLD_LTX2_GEMMA_DEVICE | auto | LTX-2 Gemma 3 12B prompt encoder placement: auto uses the GPU leased to the stage when it has more than 6 GB free, otherwise CPU; it never allocates on an unleased sibling GPU. The encoder is built one decoder layer at a time and each layer is dropped before the next, so its real peak residency is ~3.3 GB rather than the ~23 GB of BF16 weights on disk — a 24 GB card keeps Gemma on the GPU, where encoding costs seconds instead of a minute or more. cpu forces system RAM (slower, but no VRAM contention); gpu pins the assigned GPU and surfaces OOM instead of auto-offloading. An auto-placement OOM retries only Gemma on CPU while the transformer and video VAE stay on CUDA. The deprecated MOLD_LTX2_DEBUG_FORCE_CPU_PROMPT_ENCODER=1 is a one-shot-warn alias. Server preflight uses the same resolver as runtime. |
MOLD_LTX2_GEMMA_VARIANT | auto | LTX-2 Gemma 3 12B weight format: auto prefers BF16 but downgrades to a present Q4 GGUF when BF16 will not fit in available host memory (mirrors admission's max(15% of RAM, 8 GiB) headroom; disclosed at WARN, never silent; requires exactly one local .gguf — an ambiguous set refuses; hosts without a reclaimable-memory figure keep BF16), q4 (force Q4 GGUF — google/gemma-3-12b-it-qat-q4_0-gguf, ~7 GB on disk), bf16 (force BF16 split — google/gemma-3-12b-it-qat-q4_0-unquantized, ~23 GB; historical default). The memory-aware auto rule is the server's admission path; a forced-local run (mold run --local) falls back to presence-only selection — BF16 when the safetensors shards are present, Q4 GGUF otherwise. For V1, place the Q4 GGUF in your gemma_root manually — manifest auto-fetch is deferred to a follow-up. |
MOLD_LTX2_KEEP_SESSION | on | LTX-2 retains its runtime session across generations (#1099): a repeat of the same prompt serves the session's cached encoding instead of reloading the ~24 GB Gemma 3 12B prompt encoder. The retained session is small — it holds the cached encoding and a device handle, not the encoder or transformer — so a changed prompt still performs a full load, and steady-state memory is essentially unchanged. Set 0, false, or off to restore the previous behavior of rebuilding the session every generation. The session is released when the model is unloaded or evicted from the cache. |
MOLD_LTX2_INT8 | native | How LTX-2.5 int8-conv transformer linears execute on CUDA. native (default) keeps the checkpoint packed on the device (one byte per weight plus one F32 scale per output row) and runs ComfyUI's own W8A8 order — rotate the activation, dynamically quantize it per row, INT8×INT8→INT32 cuBLASLt GEMM, both scales applied in F32, bias added after the GEMM — the same execution comfy-kitchen's int8_linear performs for these exact checkpoints. dequant is the W8A16 escape hatch for A/B comparison: each linear is widened to the activation dtype per forward through the fused ConvRot kernel (one transient dense linear, never a whole block) and multiplied like an ordinary weight. Metal and CPU ignore it — they keep the widening arm regardless. The active arm is logged once as ltx2 int8 arm=… and the two settings are registered as distinct engine-shaping classes, so fingerprints and learned timings never mix them. |
MOLD_LTX2_QMATMUL | 0 | Quantized (GGUF) LTX-2.5 transformer linears on CUDA. Experimental, off by default: 1 (or true/on/yes) keeps the block linears quantized through candle's MMQ/MMVQ kernels — the same kernels that return 100% NaN for Qwen-Image and render Z-Image solid black (#1048), so the default dequantizes each linear per forward at the LTX compute dtype instead. Metal and CPU ignore it: Metal keeps QMatMul and CPU always dequantizes. The active arm is logged once as ltx2 linear kind=qmatmul / ltx2 linear kind=dequant, and the flag is registered as an engine-shaping variable so fingerprints and learned timings never mix the two arms. |
MOLD_LTX2_SPATIAL_TILE | auto | LTX-2 spatial tiling for stage-2 refinement and VAE decode, also settable per-run as --spatial-tile. auto tiles only past the 2048-px axis span the checkpoints' RoPE was trained on — so nothing mold currently renders is affected — off never tiles, and <px> or <px>:<overlap> (multiples of 32, overlap defaults to 256) forces that tile size at any resolution. A tiled stage 2 refines video only and carries stage 1's audio through unrefined, matching upstream. |
MOLD_LTX2_VAE_FORCE_FULL_DECODE | — | 1 to disable adaptive temporal chunked LTX-2 VAE decode and force one full decode pass. Useful for debugging/comparison; long or high-resolution clips may OOM. |
MOLD_LTX2_VAE_FORCE_FRAMEWISE | — | 1 to force temporal-chunk LTX-2 VAE decode even when a full decode would fit. Reduces peak VRAM at a small decode-time cost. |
MOLD_LTX2_VAE_DECODE_CHUNK_FRAMES | 4 latent frames | Positive integer number of latent frames per LTX-2 VAE decode chunk when chunked decode is active. |
MOLD_WAN_SHIFT | per tier (8.0; A14B 5.0) | Wan flow shift (upstream --sample_shift), the family's primary quality/character knob. Process-wide fallback for mold serve; a request-level sample_shift (CLI --sample-shift, TUI Advanced ▸ Video ▸ Flow shift) always wins. Must be finite and positive. |
MOLD_WAN_SOLVER | unipc | Wan sample solver fallback: unipc (FlowUniPC, the UAT'd default), euler (the solver the 4-step Lightning distills were tuned for), or dpm++ (upstream's alternative, on its own sigma grid). A request-level scheduler / --sample-solver always wins. |
MOLD_WAN_OFFLOAD_BLOCKS | auto | Trailing Wan transformer blocks to park in host RAM (#776). Unset lets the engine decide: it parks only when the render's activation budget will not fit what is free once the weights land, so a clip that already fits is unaffected. 0 disables it, and an explicit count wins over MOLD_OFFLOAD. GGUF checkpoints only — the move is a raw-byte round trip the plain and fp8 sources have no equivalent of. Measured on an RTX 4090: wan22-t2v-a14b:q5 at 81 frames / 832x480 renders in 316.3 s at a 17,322 MiB peak, where it previously OOM'd; :q8 reaches 73 frames in 2,235.0 s at 16,650 MiB. Parked blocks are rebuilt on the device once per step, so this trades wall clock for VRAM. |
MOLD_WAN_PREFETCH | 1 | 0 disables the background page-cache warm for the A14B partner expert (#802). The warm is host I/O only — the bytes are read and discarded — so it never allocates on the device and the max-of-pair VRAM invariant is unchanged. Measured on an RTX 4090 (wan22-t2v-a14b:q5, cold page cache): the mid-loop swap load falls from 8.7-22.2 s to a consistent 5.6 s. |
MOLD_WAN_STEP_CACHE | off | Wan first-block residual reuse (#801) for qualified non-distilled quality tiers: off, auto (relative-L1 threshold 0.10), or an explicit positive threshold. Block 0 runs every step; when its residual moves less than the threshold, blocks 1..N are skipped and their previous contribution is replayed. Approximate reuse is explicit because a Metal 1.3B qualification produced saturated fields with the cache on and a coherent scene with it off. It is refused with a message for Wan 1.3B and dense Wan 2.1 14B (including explicit thresholds), when a distill adapter is active, or when the schedule is under 12 steps; Wan 2.2 A14B remains qualified. Changing the threshold changes the output: a cached run is a different sample, not the same frames faster. off is bit-identical to denoising every block. The 0.10 threshold remains available for workloads where it has been visually qualified; it measured 1.85x on an RTX 4090 (wan22-t2v-a14b:q8, 33f/832x480, 20 steps). |
MOLD_WAN_STEP_PROFILE | off | Diagnostic (#775): 1 prints one per-phase, device-synced timing line per Wan denoise step (self/cross attention, ffn, quantized matmuls, casts). The syncs inflate wall time — for auditing, never production. |
MOLD_WAN_FORCE_DMMV | off | Diagnostic (#775): 1 forces the quantized matmuls onto the dequantize-per-forward fallback for A/B comparison against the MMQ fast path. Changes numerics, runtime, and transient memory; registered as an engine-shaping variable so scheduler fingerprints and learned estimates never mix forced runs with normal ones. |
MOLD_LTX2_VAE_DECODE_CONTEXT_FRAMES | auto | Positive integer latent-frame overlap/context around each LTX-2 decode chunk. Default derives from the decoder causal-conv receptive field. |
MOLD_QWEN_QMATMUL | 0 | Quantized (GGUF) Qwen-Image linears on CUDA (#1045). Experimental, off by default: 1 (or true/on/yes) keeps the checkpoint quantized and feeds candle's MMQ/MMVQ kernels directly — measured on an RTX 4090 (2026-08-14, qwen-image-2512:q4, 1024², 20 steps) those kernels currently return 100% NaN on Qwen's shapes at the very first forward, which the boundary validator catches by aborting the render, so the default stays on the dequantize-to-BF16-per-forward arm until the kernel defect is root-caused (#1048; the reproduction recipe lives in docs/architecture/qwen-mmq-nan.md). Weights the kernels cannot take (an IQ* or float-stored tensor, or a row width the dtype's MMQ block size does not divide), CPU-staged weights, and a process already forced onto the fallback by MOLD_WAN_FORCE_DMMV=1 keep the dequant arm regardless. Metal and CPU ignore it. Changes numerics slightly (int8 MMQ vs a BF16 GEMM over dequantized weights), so it is registered as an engine-shaping variable and never shares a scheduler fingerprint or learned estimate with the other setting. |
MOLD_ZIMAGE_QMATMUL | 0 | Quantized (GGUF) Z-Image linears on CUDA. Experimental, off by default: 1 (or true/on/yes) feeds candle's quantized MMQ/MMVQ CUDA kernels directly — measured on an RTX 4090 (2026-08-15, z-image-turbo:q4, 512², seed 42) those kernels return non-finite values for Z-Image's linears (the feed-forward output reaches inf from finite inputs at the first denoise step past t≈0.07) and every render comes out solid black, the same kernel defect family as MOLD_QWEN_QMATMUL (#1048). The default dequantizes each weight to the activation dtype per forward, which renders correctly and is somewhat slower. Metal and CPU ignore it and keep QMatMul. Registered as an engine-shaping variable so scheduler fingerprints and learned estimates never mix the two arms. |
MOLD_QWEN_FP8_CACHE | off | 1 keeps the widened BF16 copy of an FP8 Qwen-Image checkpoint's weights instead of re-casting them every forward (#1045). Roughly doubles the transformer's resident VRAM — an FP8 build is normally chosen precisely because that headroom is missing, so this is opt-in and only worth it on a card with room to spare. It governs the in-memory transformer only: --offload streams its blocks through candle_nn::Linear, which has no FP8 arm at all, so those blocks are always widened once at load (roughly doubling their host-RAM footprint) whatever this is set to. |
MOLD_H3_TURBO_ADAPTER | unset | Capture-scope UAT override, honored only by h3-private-uat builds. Ordinary Turbo use selects a reviewed Turbo model tag instead (minimax-h3-fl2va:comfy-pruned-int8-turbo-8step or …-turbo-4step-768p), which resolves the manifest-pinned adapter with no environment configuration; a set pair in any other build is a hard error rather than a second, silently divergent selection authority. Filesystem path to a reviewed MiniMax H3 Turbo LoRA adapter file (#1172). A Turbo tier is a LoRA overlaid on the same compact INT8 ConvRot checkpoint, so no base artifact contract relaxes; what the tier adds is a distillation triple (sampler kind, terminal-inclusive step count, video shift) plus the adapter's own authenticated identity and resident cost. The file is authenticated against the pinned digest of the tier named by MOLD_H3_TURBO_TIER, which is why the two must be set together — a path with no tier cannot be authenticated, and a tier with no path has nothing to authenticate. Setting either alone is an error. Registered as an engine-shaping variable so scheduler fingerprints and learned estimates never mix a Turbo render with a base one. |
MOLD_H3_TURBO_TIER | unset | Capture-scope UAT override, honored only by h3-private-uat builds — see MOLD_H3_TURBO_ADAPTER. Which reviewed Turbo tier the file at MOLD_H3_TURBO_ADAPTER must be. Accepts the short alias fl2v-8step (9 grid points, 8 transformer evaluations, trained at 544p, base video shift), fl2v-4step-768p (5 grid points, video shift 6), fl2v-4step-768p-v1.1 (5 grid points, video shift 6), fl2v-8step-768p (9 grid points, video shift 6), ref2v-4step (5 grid points), fl2v-4step-768p-r21 (5 grid points, video shift 6), fl2v-8step-r21 (9 grid points, video shift 12), or ref2v-4step-r21 (5 grid points, video shift 12), or the corresponding full stable id such as minimax-h3.turbo-lora.fl2v-8step-v1.0.comfyui-bf16.v1. Matching is case-insensitive. An unreviewed value is rejected and names the accepted set. Note mold counts terminal-inclusive grid points, so a "Turbo 8-step" checkpoint is 9 mold steps and exactly 8 forwards. Registered as an engine-shaping variable. |
MOLD_H3_CONDITIONER_CACHE | 512 (MiB) | Byte budget for the in-process cache of MiniMax H3's Qwen conditioner output (#814). A repeated prompt against the same first-frame/reference set, conditioner route, and device serves the prior render's conditioning tensor instead of reloading and re-encoding the 15.7 GB checkpoint — the output is bit-identical, disclosed as prompt conditioning [cache hit]. off, 0, false, no, or disabled (case-insensitive) turns the cache off; <MiB> sets the resident-bytes budget, minimum 1 and clamped to 65536 (64 GiB) so a typo cannot produce a budget no entry can reach; an unparseable value keeps the default and warns; unset defaults to 512 MiB. Read straight from the environment like MOLD_LTX2_KEEP_SESSION, and deliberately not registered as an engine-shaping variable — a hit restores the identical tensor a miss would have produced, so it moves wall clock and host residency only, never weights, device choice, or numerics. Inert in h3-private-uat capture-scope builds so a capture run always exercises the conditioner. |
Prompt Expansion
| Variable | Default | Description |
|---|---|---|
MOLD_EXPAND | — | 1 to enable expansion |
MOLD_EXPAND_BACKEND | local | local or OpenAI-compatible URL |
MOLD_EXPAND_MODEL | qwen3-expand:q8 | LLM model for expansion |
MOLD_EXPAND_TEMPERATURE | 0.7 | Sampling temperature |
MOLD_EXPAND_THINKING | — | 1 to enable thinking mode |
MOLD_EXPAND_SYSTEM_PROMPT | — | Custom system prompt template |
MOLD_EXPAND_BATCH_PROMPT | — | Custom batch prompt template |
Server
| Variable | Default | Description |
|---|---|---|
MOLD_GPUS | all | all, none (maintenance), or comma-separated ordinals/stable cuda:/metal:/GPU-/MIG- IDs. Prefer IDs from /api/devices in persistent config. See Multi-GPU |
MOLD_QUEUE_SIZE | 200 | Jobs hydrated into the runtime dispatch window; overflow returns HTTP 503 QUEUE_FULL with Retry-After. The durable SQLite backlog is not bounded by this value — it holds every admitted job until it runs, is cancelled, or is swept |
MOLD_OUTPUT_DIR | ~/.mold/output | Image output directory (set empty to disable) |
MOLD_THUMBNAIL_WARMUP | — | 1 to prebuild gallery thumbnails at server startup (default: disabled) |
MOLD_WEB_DIR | — | Override the web gallery SPA bundle location. First resolved path among this, $XDG_DATA_HOME/mold/web, ~/.mold/web, <binary dir>/web, and ./web/dist wins |
MOLD_DB_PATH | MOLD_HOME/mold.db | Override the SQLite gallery metadata DB location |
MOLD_DB_DISABLE | — | 1 to disable the SQLite metadata DB entirely — server and CLI fall back to filesystem walks. mold serve cannot generate in this mode: the durable queue is the only admission path, so every generation route answers 503 DURABLE_ADMISSION_UNAVAILABLE. Gallery, models, catalog, and queue routes still serve, and /health stays 200. The same refusal applies with gallery output disabled (MOLD_OUTPUT_DIR=) or a non-authoritative dispatcher (MOLD_DISPATCH_MODE=legacy / observe) |
MOLD_GALLERY_TRASH_RETENTION_DAYS | 30 | Days a trashed print stays in <output_dir>/.trash/ before the sweeper purges it; 0 keeps trashed prints forever (max 3650). Env override of the gallery.trash_retention_days key — see Library trash |
MOLD_GALLERY_AUTHORITY_LOG | 0 | 1 to WRITE the gallery archive authority's version-3 delta log instead of a whole-snapshot checkpoint per publication. Off by default because it changes the on-disk format of the home: a mold older than 0.29 cannot publish against a version-3 store. Reading one needs no switch. Env override of the gallery.authority_log key — see Gallery authority storage and Shared homes and older binaries |
MOLD_QUEUE_HELD_RETENTION_DAYS | 30 | Days a held durable queue row is kept before the sweeper purges it and releases its staged media, and days a fully settled batch summary is kept after its last child settled; 0 keeps both forever (max 3650). Env override of the queue.held_retention_days key — see Held-queue retention |
MOLD_CORS_ORIGIN | — | Restrict CORS to specific origin |
MOLD_API_KEY | — | API key for authentication (single key, comma-separated, or @/path/to/keys.txt) |
MOLD_DESTINATION_API_KEY | — | Credential for the DESTINATION host when handing a queued job to another machine (mold queue handoff --to); independent of the source MOLD_API_KEY |
MOLD_RATE_LIMIT | — | Per-IP rate limit for generation endpoints (e.g., 10/min, 5/sec, 100/hour) |
MOLD_RATE_LIMIT_BURST | — | Burst allowance override (defaults to 2x rate, capped at 100) |
MOLD_MAX_CACHED_MODELS | 3 | LRU model-cache capacity (range 1..=16). At most one entry stays GPU-resident; the rest are parked in CPU RAM. Out-of-range values warn and fall back to default. |
MOLD_CACHE_IDLE_TTL_SECS | 1800 (30 min) | Idle timeout for parked cache entries (range 60..=86400). Untouched entries are evicted past this TTL. |
MOLD_QUEUE_LOOKAHEAD_BUFFER | 8 | Server queue lookahead size (range 1..=64). The dispatcher peeks this many jobs ahead to honour locality. |
MOLD_QUEUE_MAX_DEFERRALS | 3 | Per-job starvation budget (range 0..=32). A job can be deferred this many times before forced pickup. |
MOLD_MALLOC_TRIM | 1 (Linux/glibc) | 0 disables the post-generation malloc_trim(0) call. Linux-only; reclaims arena pages after large GGUF+LoRA rebuilds. It runs after the print is saved and the completion is queued, so it is off the client's wall clock; the generation memory delta log line reports its cost as trim_ms. |
MOLD_PNG_ENCODING | fast | How much CPU a saved PNG is worth. fast uses fdeflate's PNG-tuned ultra-fast deflate; balanced is zlib level 6, the pre-0.29 behaviour. PNG is lossless either way, so this only trades encode time against file size and never changes a pixel — measured on a 512² photograph: 1.7 ms / 322 KB against 59.0 ms / 304 KB. An unrecognized value is fast. |
MOLD_FLUX_DELTA_CACHE | 1 | 0 disables the CPU-side FLUX LoRA delta cache (~25 GB host RAM on typical FLUX LoRAs). Disabling forces a sub-second B@A·scale recompute on each rebuild. |
MOLD_FLUX_KEEP_TRANSFORMER | budgeted | Residency of the FLUX.1 / FLUX.2 transformer across renders. The default changed: instead of dropping it before every VAE decode, mold now measures the card — the resident checkpoint, this render's denoise workspace, the decode workspace, and an allocator margin against usable free VRAM — and keeps it when the four fit, which saves a full reload (8.4 s for a FLUX.1 Q8, 34 s for a FLUX.2 Q8) on every warm render. 0 (also off, false, no) forces the drop even where the budget fits, on both families; 1 (also on, true, yes) is accepted and means the same thing as the default, because an explicit keep has always had to yield to a card that cannot afford it and the budget is now what expresses that for everyone. Anything else reads as unset. The execution fingerprint records the request, not the outcome — 0 is its own execution class and unset/1 share the other, so a forced drop is never filed with a budgeted render; what the budget actually decided for a given render is a per-render VRAM measurement and is reported in the server log (Transformer kept resident / Transformer dropped before VAE decode), not in the fingerprint |
MOLD_HOST_RAM_ZFS_ARC | on | 0 / false / no / off stops counting OpenZFS's evictable ARC as host-RAM headroom. The credit is published beside MemAvailable as host_memory.reclaimable_zfs_arc_bytes. Turn it off in a container that can read the host's arcstats but is capped by its own cgroup. |
Durable queue and shutdown
A queued generation survives a server restart: it is recorded in mold.db before it is queued and replayed automatically at the next start, under its original job id. GET /api/capabilities reports queue.durable_queue — false on a host with server gallery output disabled, which cannot promise durability for anything — and each row in GET /api/queue reports whether that particular job is durable — a job with no gallery target or one whose request exceeds the payload ceiling runs normally but is not replayed.
| Variable | Default | Description |
|---|---|---|
MOLD_QUEUE_JOURNAL_DISABLE | — | 1 turns the durable queue off entirely. Jobs still run; nothing survives a restart, and queue.durable_queue reports false. |
MOLD_QUEUE_JOURNAL_MAX_BYTES | 33554432 (32 MiB) | Ceiling on one recorded request. A larger request (an inline video, say) runs normally and is reported durable: false rather than being half-persisted. |
MOLD_QUEUE_MAX_DISPATCH_ATTEMPTS | 2 | How many times a worker may start a job before it is held instead of retried. Charged only when a worker actually claims the job, so a job that merely waits behind a long render through many restarts is never charged. |
MOLD_QUEUE_MAX_REPLAY_SEEN | 10 | How many restarts may replay a job that never starts before it is held. Sized for a crash loop; ordinary deploys never approach it. |
MOLD_QUEUE_ADOPT_OWNER | — | Adopt a specific orphaned queue by its owner id, printed in the startup warning. Only needed when several retained queues share one MOLD_HOME and none matches this server. |
MOLD_SHUTDOWN_ABORT_SECS | 45 | Hard deadline for the whole shutdown after SIGTERM. The running generation is aborted at its next checkpoint and requeued; queued work is retained and replayed. If shutdown overruns, mold serve ends the process rather than wait — a cold model load is not interruptible, and hanging past systemd's stop timeout is what used to get the server SIGKILLed mid-write. |
A held job is listed by GET /api/queue with state: "held" and a reason, and is never started automatically — it is waiting for you to look at it. Clear one with DELETE /api/queue/{id}.
When restart-safe request media turns itself off
A request that carries bytes — a source image, an identity photo — only survives a restart if the encrypted store under $MOLD_HOME/queue-media is usable. That directory holds the master key, so Mold refuses it unless it is owned by the service user with mode 0700, and a refusal switches the feature off for the life of the process while ordinary media-free durability keeps working. Anything that walks the Mold data root widens it: an ACL pass, a chmod -R, a restore that does not preserve modes, an rsync without -p.
The degradation is reported in three places, so a missed startup log is not the end of it:
curl http://localhost:7680/health
# {"status":"degraded","degraded":["durable_media"]}
curl -H "X-Api-Key: $MOLD_API_KEY" http://localhost:7680/api/status | jq .durable_media
# { "available": false, "reasons": [
# "owner media store unavailable: /srv/mold/queue-media must be a current-user-owned
# 0700 directory: found mode 0770 (expected 0700); repair with: chmod -- 0700 '/srv/mold/queue-media'" ] }/health always answers 200 while the server is serving — a degraded subsystem does not stop it rendering — so alert on the degraded array rather than the status code. Run the repair the reason names and restart the server; Mold never widens or narrows these modes on your behalf, so a permission change you did not make stays visible.
Sharing one MOLD_HOME between servers
Each server owns its queue through a record under $MOLD_HOME/queue-owners/, so two servers on different ports never replay each other's work. A server recognises its own queue after a restart, including after a port change, in every case but one: if several retained queues are present and none was last used by this server, it cannot tell which is its own. It then starts with a fresh queue and warns at startup, naming each orphaned owner with its last-known instance and its queued and held row counts. Adopt one deliberately with:
MOLD_QUEUE_ADOPT_OWNER=<owner-id-from-the-warning> mold serveThere is one case the server deliberately gets wrong rather than leave work stranded. If exactly one retained queue is present, nobody is running it, and it was last used by a different server, the starting server adopts it and says so loudly in the log. Nearly always that is correct — it is the same server coming back on a changed port, which is precisely what the queue is designed to survive, and what makes the "this job will finish on the host" message clients show at shutdown true rather than a lie.
But a genuinely new second server, started while the first is stopped against a MOLD_HOME that holds one retained queue, cannot be distinguished from that and will adopt the queue too. It runs the other server's jobs. The trade is deliberate: a silently stranded queue is worse than an announced adoption, because nothing tells the user their job is never coming. If you are adding a second server to an existing MOLD_HOME, start it while the first is running, or drain the first server's queue before you do.
Under systemd, set the budget through the NixOS module rather than the environment, so the unit's stop timeout stays derived from it:
services.mold.shutdown.abortSeconds = 45; # TimeoutStopSec becomes 105sDo not set TimeoutStopSec=infinity: with a durable queue the right response to a wedged worker is to exit and replay, not to hang the deploy.
When the deadline expires the server exits with status 0 for an ordinary stop that merely overran, and 1 for a shutdown triggered by a fatal CUDA error so Restart=on-failure brings it back. The desktop app's built-in engine never does this — it runs inside a process it does not own, so its budget only stops it waiting.
Shared homes and older binaries
A MOLD_HOME shared between processes — a scratch or second server beside a running one, an NFS/ZFS root two machines mount, a canary beside production — must be served by binaries of the same or a newer storage format, never a mix in which one is newer than another.
mold's on-disk stores (the gallery authority, the durable queue, the metadata database) carry a format version and are read forward, not backward: a newer binary reads what an older one wrote and upgrades the store to its own version the first time it starts against that root, and the upgrade is in place and one-way. From that moment an older binary still pointed at the same home cannot read it, and stopping the newer process does not put it back. The symptom is not a startup failure — the older server keeps answering /api/capabilities and /api/models — it is the affected subsystem refusing at the point of use, so a gallery read or a publication starts failing while the host still looks alive.
Practical rules for a shared home:
- Upgrade every binary that uses the home together, oldest first out of service. A canary that shares a production home is a production upgrade.
- Give a test or scratch server its own
MOLD_HOME(and its ownMOLD_OUTPUT_DIR). That is the only configuration in which trying a newer build carries no risk to the running one. - Before starting a newer build against a shared home, back up the store. Rolling forward is supported; rolling back means restoring that backup, and anything published after the upgrade is not in it.
- Read the release notes for the version you are moving to: a release that changes a store's format says so, and says which subsystem is affected.
The gallery archive authority is the one store that does not work this way, and deliberately so. Since 0.29 it has a second format (storage version 3, an append-only delta log) that a new binary can read but only writes when you ask for it — see Gallery authority storage. A default build leaves a version-2 store exactly as it found it, so starting a newer mold against a shared home no longer locks an older one out of it. When you do opt in, the upgrade writes a NEW store beside the version-2 one rather than over it, and mold system gallery-authority downgrade folds it back, so the rollback that the rules above say to keep a backup for is a command instead.
Upscaling
| Variable | Default | Description |
|---|---|---|
MOLD_UPSCALE_MODEL | — | Default upscaler model for mold upscale |
MOLD_UPSCALE_TILE_SIZE | — | Tile size for memory-efficient upscaling (0 to disable tiling) |
Auth
| Variable | Default | Description |
|---|---|---|
HF_TOKEN | — | Default Hugging Face token for gated models; web Settings can override it until cleared |
CIVITAI_TOKEN | — | Default Civitai token for gated models; web Settings can override it until cleared |
Third-party model licenses
Some auxiliary weights carry terms that Mold's MIT license does not cover. The InsightFace antelopev2 face models that PuLID identity conditioning needs are licensed for non-commercial research only, so Mold refuses to download them — from mold pull, the server's auto-pull, or any client-triggered download — until you record acceptance once per MOLD_HOME:
mold pull pulid-flux --accept-license insightface-antelopev2
mold pull pulid-sdxl --accept-license insightface-antelopev2There are two PuLID bundles — pulid-flux for FLUX.1 checkpoints and pulid-sdxl for SDXL ones — and they share the one antelopev2 acceptance, so recording it for either unblocks both. See Identity.
The command prints the restriction and both terms URLs before it writes the record. Acceptances live in owner-only $MOLD_HOME/license-acceptances.json.
The terms are pinned to an exact upstream commit, not to a branch: Mold stores the commit-addressed URL of the license text alongside its SHA-256, and verified that pair when the pin landed. A commit URL serves the same bytes forever, so the digest can never drift away from the document it describes — which a master link would, quietly leaving you consented to text that had since been rewritten. Each acceptance is bound to that (url, sha256) pair, so a Mold release that re-pins a license to a newer upstream revision invalidates your existing acceptance and asks again with the new text.
Recording an acceptance is entirely offline — Mold never fetches the license text, so --accept-license works on an air-gapped host. A refused download names the license and the exact command to run; there is no environment-variable bypass. See THIRD_PARTY_NOTICES.md for the full notice, including the pinned commit and digest.
Which machine records the acceptance
The acceptance has to live on the machine that does the downloading, and mold pull follows the pull:
- A server answers at
MOLD_HOST(including the localmold serve): the id is sent with the request and the server writes it into its own$MOLD_HOME. Nothing is recorded on the calling machine. - No server, or
--local: the pull runs here, so the acceptance is recorded here.
Either way the terms are printed before the request goes out — and they are the terms of the machine that will record them. When a server will do the recording, mold pull reads that server's GET /api/licenses, displays what it returned, and sends back exactly that; the server refuses anything else. This matters because the two sides can be on different Mold releases pinning different revisions of the same license, and consent has to mean the document that was actually shown.
Run mold licenses to see what needs accepting and which root was read:
mold licenses # asks the server at MOLD_HOST when one answers
mold licenses --local # this machine's own acceptances, without askingmold licenses reads this machine only when nothing is listening at MOLD_HOST, or when you pass --local. If a server is there but the request fails — authentication, a 5xx, an unreadable body — the command reports that against the host rather than quietly showing you a different machine's acceptances.
Over HTTP, GET /api/licenses returns each license with accepted and required_by, and POST /api/downloads / POST /api/models/pull accept an additive accept_licenses array of { id, url, sha256 } entries. A gated download without one is refused with 403 and code LICENSE_NOT_ACCEPTED; terms the server does not pin are refused with 409 and code LICENSE_TERMS_MISMATCH. Both carry a structured license object — including the server's own url and sha256 — that a UI can build its own prompt from. Servers that support this advertise capabilities.licenses: true; older ones do not, and can only be accepted by running mold pull --accept-license in a shell on that host.
Gallery Metadata Database
mold persists generation metadata in a SQLite database at MOLD_HOME/mold.db (override with MOLD_DB_PATH). All three surfaces — the CLI's local generation path, the TUI, and the HTTP server — write a row per saved file: prompt, negative prompt, model, seed, steps, guidance, dimensions, LoRA, scheduler, the file's mtime/size, the generation duration, and a source column (server / cli / tui / backfill / unknown).
The DB also stores the full generation metadata JSON for rows written by current versions, so gallery clients can recreate outputs with advanced options such as LoRA stacks, ControlNet settings, CFG++, output format, and LTX-2 audio/video pipeline controls.
The DB powers /api/gallery so listings stay fast on large directories (no per-request file walk) and surface metadata for formats that don't embed it (mp4, gif, webp). PNG / JPEG outputs still get the existing embedded mold:parameters chunk in addition to the row.
On server startup the DB runs an asynchronous reconciliation pass:
- new files in
MOLD_OUTPUT_DIRget rows added (synthesizing metadata from the filename when no embedded chunk is present) - rows whose backing files have been removed (manual
rm, file manager, etc.) get pruned - size/mtime changes trigger a row refresh
At each open, mold runs SQLite's quick_check. If SQLite reports a corrupt or non-database file at startup—or an indexed gallery query discovers corruption later—mold serializes recovery across local mold processes, copies the database and any WAL/SHM sidecars to mold.db.corrupt-<timestamp>*, replaces the live schema through SQLite's coordinated online-backup API, and rebuilds gallery rows from the files in MOLD_OUTPUT_DIR. The quarantined files remain available for manual inspection or salvage. Because the same database also contains user preferences and prompt history, those values reset unless they are manually recovered from the quarantined copy.
Set MOLD_DB_DISABLE=1 to opt out — both surfaces fall back to the filesystem walk + embedded-metadata behavior from before. The NixOS module exposes the same toggle:
services.mold = {
enable = true;
metadataDb.enable = false; # opt out
# metadataDb.path = "/var/lib/mold/custom.db"; # override location
};Library trash
Deleting a print from the Library (desktop, web, iPhone, TUI, or DELETE /api/gallery/image/:filename) moves it to the host's trash instead of removing it: the file goes to <output_dir>/.trash/<filename> next to a small <filename>.trash.json tombstone, the gallery row keeps its title, tags, favorite flag, and collection membership, and the print can be restored until the retention sweep purges it. The sweep runs at server startup and hourly; mold trash sweep and POST /api/gallery/trash/sweep run it on demand. Appending ?permanent=true to the delete (or "Delete forever" in a client) skips the trash.
| Key | Env var | Default | Description |
|---|---|---|---|
gallery.trash_retention_days | MOLD_GALLERY_TRASH_RETENTION_DAYS | 30 | Days a trashed print is kept before it is purged. 0 keeps trashed prints forever; max 3650. |
mold config set gallery.trash_retention_days 7 # purge a week after trashing
mold config set gallery.trash_retention_days 0 # keep trashed prints until emptied by hand
mold config where gallery.trash_retention_days # → dbThe key lives in mold.db (profile-scoped, gallery. prefix) and is read fresh on every sweep, so a change takes effect without a restart. It is per host: each mold serve keeps its own trash and its own retention, and the desktop, web, and iPhone apps edit a remote machine's value through that host's /api/config. GET /api/capabilities advertises the effective value as gallery.trash.retention_days; with MOLD_DB_DISABLE=1 there is no trash and capabilities.gallery.trash is absent, so delete is permanent as before.
Gallery authority storage
The gallery archive authority is the record of which prints the host has published and what each one's bytes hash to. It has two on-disk formats:
- Version 2 (default) writes a whole checkpoint of the index on every mutation. Simple, and the cost of publishing one print grows with the size of the library.
- Version 3 writes an append-only delta log beside a periodic checkpoint, so publishing a print costs the same on a library of ten as on one of ten thousand.
| Key | Env var | Default | Description |
|---|---|---|---|
gallery.authority_log | MOLD_GALLERY_AUTHORITY_LOG | false | Write storage version 3. Reading a version-3 store never needs this set. |
mold config set gallery.authority_log true # opt this host in to version 3
mold system gallery-authority status # what this machine's store is
mold system gallery-authority downgrade # fold v3 back to v2, offlineWhy it is opt-in. The format is a property of the $MOLD_HOME, not of the process, and a mold older than 0.29 reads version 2 only — it refuses publication outright against a version-3 store. Turning this on is therefore a decision about every binary that shares the home, so make it only where they are all 0.29 or newer. Reading is never gated: any build that finds a version-3 store understands it.
A shared home keeps two separate indexes, it does not merge them. The upgrade writes a new gallery-authority-v3 directory and leaves the gallery-authority-v2 one untouched, which is what makes the rollback free — but it also means that while a version-2 writer and a version-3 writer publish to one home, each maintains its own index and the two drift apart. That is the reason the switch exists rather than a defect in it: the setting is for homes where every binary is new enough, not for mixed ones. See Shared homes and older binaries.
Rolling back. Run mold system gallery-authority downgrade with the NEWER build, before starting an older binary against the home. It takes the gallery bookkeeping lock and the gallery's writer lease, replays the delta log onto the checkpoint, rewrites checkpoint, backup, and marker at version 2, and verifies by reading the result back; the retired version-3 directory is parked beside the store rather than deleted. It is idempotent, and it refuses rather than guesses if a mutation is still pending or the log tail is torn — start mold serve once with a version-3-capable build to let recovery resolve those, stop it, then downgrade. Both subcommands take --output-dir (defaulting to this machine's configured gallery) and --json.
Stop every writer first, and the command checks. A mold process that can publish to a gallery — a running mold serve, a local mold run, the desktop app — holds a writer lease on it (.mold-gallery-writer.lease, a hidden file in the gallery directory) for as long as that process lives, and downgrade refuses while one is held:
$ mold system gallery-authority downgrade
Error: a mold process is still publishing into the gallery archive authority in
/storage/mold/output — the lease records `mold serve` (pid 31245), held for 4m 2s
(since epoch ms 1789202630592). Stop `mold serve` (and any local `mold run` or
desktop app) on this $MOLD_HOME, then run the downgrade again. Nothing has been
changed.mold system gallery-authority status prints writer lease: held (pid …) for the same reason, so you can see it before you try. The lease is shared, so several servers keep sharing one home.
A process that stops cleanly takes its lease file with it. One killed outright leaves the file but not the lock, which status reports as writer lease: stale: it blocks nothing, and the next downgrade removes it — that command's whole job is to leave the gallery in a state an older mold can open, so it ends by clearing the lease rather than leaving mold's own file behind.
Held-queue retention
A durable generation that cannot proceed is held rather than failed: it stays listed in GET /api/queue, keeps its staged request media, and waits for someone to fix the cause and retry it. A hold nobody ever returns to is not waiting for anything, so it is swept.
| Key | Env var | Default | Description |
|---|---|---|---|
queue.held_retention_days | MOLD_QUEUE_HELD_RETENTION_DAYS | 30 | Days a held queue row is kept before it is purged, and days a settled batch summary is kept after its last child settled. 0 keeps both forever; max 3650. |
mold config set queue.held_retention_days 7 # purge a week after the hold
mold config set queue.held_retention_days 0 # keep held work (and settled batch summaries) until removed by hand
mold config where queue.held_retention_days # → dbAge is measured from when the row was held, not when it was admitted, so work that waited a week in the queue and held yesterday still gets its full window. The server sweeps hourly and reads the key fresh on every pass; POST /api/queue/held/sweep runs one pass on demand.
Purging a held row releases the encrypted request media that row pinned, and settles its batch child as failed so a reconnecting client still sees a terminal outcome instead of a print that appears never to have been admitted. A retry or a cancel that lands before the sweep wins — an explicit decision always outranks retention.
The same key bounds settled batch summaries. Once every print in a batch has finished, failed, or been cancelled, the batch record is only a receipt for a client reconnecting after a dropped stream, and it holds no media. The sweeper purges it once its newest child settlement is older than queue.held_retention_days (a batch with a held child waits for the held sweep to settle that child first), and POST /api/generation-batches/sweep runs that pass on demand. A purged batch answers 404 GENERATION_BATCH_NOT_FOUND, which clients treat as "missing" without reopening work that already finished.
Auto-tagging titled prints
| Key | Env var | Default | Description |
|---|---|---|---|
generate.auto_tag_title | — | true | Whether a titled CLI or TUI run also tags the print with its title slug. |
mold config set generate.auto_tag_title false # stop tagging titled prints
mold config where generate.auto_tag_title # → dbWith it on, mold run "a village" --title "Smurf village" also files the print under the tag smurf-village and discloses it on stderr (filing under tag "smurf-village"). mold run --no-auto-tag overrides the setting for one invocation.
This is deliberately a client setting with no env override: it shapes what the CLI puts in a request, not how a server behaves. The server never auto-tags — it cannot tell a title a person typed from one a script generated, and a host quietly adding tags to every print that crossed it would be surprising from every other machine on the fleet.
Advanced
Device and Path Overrides
| Variable | Default | Description |
|---|---|---|
MOLD_DEVICE | — | Force device placement, currently cpu for debugging |
MOLD_TRANSFORMER_PATH | — | Override transformer weights path |
MOLD_LOW_NOISE_TRANSFORMER_PATH | — | Override the low-noise expert of a Wan 2.2 A14B pair |
MOLD_VAE_PATH | — | Override VAE weights path |
MOLD_SPATIAL_UPSCALER_PATH | — | Override LTX spatial upscaler path |
MOLD_TEMPORAL_UPSCALER_PATH | — | Override LTX temporal upscaler path |
MOLD_DISTILLED_LORA_PATH | — | Override the default LTX-2 distilled LoRA path |
MOLD_LOW_NOISE_DISTILLED_LORA_PATH | — | Override the distill for a Wan A14B low-noise expert |
MOLD_T5_PATH | — | Override T5 encoder path |
MOLD_CLIP_PATH | — | Override CLIP-L encoder path |
MOLD_CLIP2_PATH | — | Override CLIP-G encoder path for SDXL |
MOLD_T5_TOKENIZER_PATH | — | Override T5 tokenizer path |
MOLD_CLIP_TOKENIZER_PATH | — | Override CLIP-L tokenizer path |
MOLD_CLIP2_TOKENIZER_PATH | — | Override CLIP-G tokenizer path for SDXL |
MOLD_TEXT_TOKENIZER_PATH | — | Override generic text tokenizer path for Qwen/Z-Image |
MOLD_DECODER_PATH | — | Override Wuerstchen decoder weights path |
MOLD_QWEN2_VARIANT | auto | Qwen-family Qwen2.5-VL encoder: auto, bf16, q8, q6, q5, q4, q3, q2 |
MOLD_QWEN2_TEXT_ENCODER_MODE | auto | Qwen-family placement mode: auto, gpu, cpu-stage, cpu |
These are mainly useful for custom local model layouts, manual debugging, or testing alternative weight files without editing config.toml.
Per-component device placement
Override which device (CPU or a specific GPU) runs each part of the diffusion pipeline. All variables accept auto (preserve the engine's VRAM-aware default), cpu, gpu (= gpu:0), or gpu:N for a process-local ordinal. They also accept the exact opaque ID reported by GET /api/devices, such as cuda:0123… or metal:default, either directly or as device:cuda:0123…. Durable IDs survive ordinal reordering; raw NVIDIA GPU-/MIG- UUID selectors are reserved for --gpus / MOLD_GPUS startup selection.
| Variable | Applies to | Notes |
|---|---|---|
MOLD_PLACE_TEXT_ENCODERS | Every model family (Tier 1) | Single knob that moves every text encoder slot as a group. Picking cpu frees the transformer's full VRAM budget without triggering block offload. |
MOLD_PLACE_TRANSFORMER | FLUX, Flux.2, Z-Image, Qwen-Image | Per-component override. Interacts with MOLD_OFFLOAD — resident and streamed blocks target the chosen ordinal. |
MOLD_PLACE_VAE | FLUX, Flux.2, Z-Image, Qwen-Image | Decode stage; CPU is fine for preview, GPU is faster. |
MOLD_PLACE_T5 | FLUX | Per-encoder override; unset falls through to MOLD_PLACE_TEXT_ENCODERS. |
MOLD_PLACE_CLIP_L | FLUX | Per-encoder override. |
MOLD_PLACE_CLIP_G | SDXL and others that use CLIP-G | Per-encoder override. |
MOLD_PLACE_QWEN | Flux.2, Z-Image, Qwen-Image | Per-encoder override for the Qwen text encoder. |
For local CLI generation, precedence (highest wins) is CLI flag (--device-text-encoders, --device-vae, …) → environment variable → [models."name:tag".placement] TOML block → engine auto. For server requests, an explicit request placement is the complete placement decision; otherwise environment values override the persisted per-model placement and unspecified components remain auto. The server normalizes this once before admission, so validation, scheduling, and inference consume the same placement.
Scheduler V2 resolves that normalized shape into a concrete admission plan per eligible device before dispatch. Plans include exact artifact paths and identity fingerprints, materialized placement, inferred precision/quantization metadata, planned load/offload mode, sampled free-VRAM peak, and incremental host RAM. Explicit CPU/device values never become scoring hints: an unavailable device or components pinned across different GPUs blocks the request. Automatic CPU placement is considered only under measured/static memory pressure and only for a family/component path Mold implements. The GPU owner validates the selected device and artifacts again before CUDA work; a changed artifact invalidates the plan instead of being silently substituted.
Scheduler observations keep setup separate from execution. Typed cold-load, warm-reload, prompt-encode, denoise, VAE, and upscale timings feed bounded learned estimates; metadata schema v15 persists runtime independently so a candidate receives exactly its cold or warm setup charge. Face-identity extraction is one of those phases as of schema v22: a request that conditions on a photograph runs the whole PuLID face stack on its own leased GPU before the checkpoint loads, reports it as an Extracting face identity stage, and feeds its measured duration back into the estimates. Its device memory is part of the plan the scheduler admits, so a conditioned render queues against what it needs; on Apple Silicon that reservation is made once against unified memory rather than separately against host RAM. Within one server run, the identity for a photograph already seen is reused from memory instead of being re-extracted — keyed on the photograph and on every model file involved, so a repaired bundle invalidates it — and it is never written to disk. Multi-host Create uses POST /api/generate/placement-preview as a read-only final feasibility check for ordinary generation. A planned response can name known encoder dependencies in pending_downloads — and, for a request that conditions on a face, the five PuLID identity assets under the identity_adapter, identity_vision_encoder, face_detector, face_recognizer, and face_parser kinds; those downloads and the low-confidence estimate include only devices selected by that candidate plan. The preview does not fetch them, uses a separate registry-identity fingerprint, and admission recomputes the plan after the files land. Cold installed Civitai and Hugging Face IDs resolve from contained local sidecars, with their synthesized runtime configuration carried through scheduling and final GPU validation even if the server refreshes its model list. An infeasible response can name absent manifest files in missing_components so clients can explain the repair instead of discarding the server's reason. Current chain and local prompt-expansion/post-generation-upscale utility previews deliberately return non-authoritative unsupported: those paths are not advertised as exact until their real device/CPU fallbacks are represented.
Forced-local batches (mold run --local --batch N) use the same deterministic assignment core across all GPUs selected by --gpus/MOLD_GPUS. There is no two-GPU limit; a one-item run keeps the existing best-free-GPU selection.
The web UI's Placement panel, the desktop app's Settings → Advanced placement editor, the GET/PUT/DELETE /api/config/model/:name/placement routes (read a saved default, save one, clear one — GET returns 404 when none is saved), and mold run --device-* flags all write/read the same shape, so any surface can drive it.
Tier 2 per-component controls are intentionally gated: families other than FLUX, Flux.2, Z-Image, and Qwen-Image only honor Tier 1 (MOLD_PLACE_TEXT_ENCODERS) — their engines don't yet split encoder/transformer/VAE across devices. Setting the advanced variables on a Tier 1-only family is a no-op (the web UI hides the Advanced disclosure for those families so it isn't misleading).
For Qwen-Image and Qwen-Image-Edit:
- CUDA
autoprefers BF16 when enough text-encoder headroom remains, and falls back to quantized GGUF variants for local sequential, resident, and edit-conditioning paths when BF16 would be too heavy. - Metal/MPS
autoprefers the quantized Qwen2.5-VL GGUF encoder path to reduce memory pressure during prompt encoding. qwen-image-editstill loads the Qwen2.5-VL vision tower for image conditioning, but quantizedMOLD_QWEN2_VARIANTvalues keep the language side smaller and stage the vision weights only when needed.
Debug and Family-Specific Knobs
| Variable | Default | Description |
|---|---|---|
MOLD_SD3_DEBUG | — | Enable verbose SD3.5 pipeline logging |
MOLD_QWEN_DEBUG | — | Enable verbose Qwen-Image pipeline logging |
MOLD_ZIMAGE_DEBUG | — | Enable verbose Z-Image pipeline logging |
MOLD_LTX_DEBUG | — | Enable verbose LTX Video / LTX-2 pipeline logging |
MOLD_LTX_DEBUG_FILE | /tmp/mold-ltx2-debug.log | Append LTX Video / LTX-2 debug output to a file |
MOLD_LTX_DEBUG_COMPARE_UNCOND | — | Log conditional vs unconditional LTX-2 prompt-context comparisons |
MOLD_LTX_DEBUG_ALT_PROMPT | — | Use an alternate prompt string for LTX-2 prompt-sensitivity debugging |
MOLD_LTX_DEBUG_DISABLE_CROSS_ATTENTION_ADALN | — | Debug-only LTX-2 switch to bypass cross-attention AdaLN modulation |
MOLD_LTX2_DEBUG_DISABLE_TRANSFORMER_GATED_ATTENTION | — | Debug-only LTX-2 switch to bypass transformer gated attention |
MOLD_LTX2_DEBUG_FORCE_CPU_PROMPT_ENCODER | — | Deprecated alias for MOLD_LTX2_GEMMA_DEVICE=cpu. Emits a one-shot warn at runtime; remove in favor of the new knob. |
MOLD_LTX2_DEBUG_TIMINGS | — | Emit native LTX-2 pipeline, phase, and denoise timing summaries for optimization work |
MOLD_LTX2_DEBUG_STAGE_PREFIX | — | Write decoded native LTX-2 stage artifacts using this filename prefix |
MOLD_LTX2_DEBUG_BLOCKS | — | Emit per-block native LTX-2 transformer debug logs |
MOLD_LTX2_DEBUG_BLOCK_DETAIL | — | Restrict detailed native LTX-2 block logging to a specific transformer block index |
MOLD_LTX2_DEBUG_LOAD_BLOCKS | — | Log native LTX-2 transformer block loading details |
MOLD_LTX2_FORCE_EAGER | — | Force eager native LTX-2 transformer loading instead of layer streaming |
MOLD_LTX2_FORCE_STREAMING | — | Force native LTX-2 transformer layer streaming |
MOLD_LTX2_ATTN_F32 | — | Pin LTX-2 self-attention on the legacy F32 chunked path instead of the BF16 MOLD_ATTN dispatcher. Output-changing A/B control for #735; prints record the executed route as attention_path. |
MOLD_LTX2_FP8_INPUT_SCALE_MODE | skip | Debug override for native LTX-2 FP8 input-scale handling (skip, emulate, divide, multiply) |
MOLD_LTX2_FP8_WEIGHT_SCALE_MODE | apply | Debug override for native LTX-2 FP8 checkpoint weight-scale handling (apply, skip, scaled-mm) |
MOLD_WUERSTCHEN_DEBUG | — | Enable verbose Wuerstchen pipeline logging |
MOLD_WUERSTCHEN_DECODER_GUIDANCE | 0.0 | Override decoder-stage CFG guidance for Wuerstchen |
These are intended for troubleshooting and development rather than normal use.
Build-Time Metadata
| Variable | Default | Description |
|---|---|---|
MOLD_FULL_VERSION | — | Internal build-time version string embedded into CLI output |
This variable is set during the build and is not normally configured by users at runtime.
