Skip to content

NixOS

mold provides a NixOS module for declarative server and Discord bot deployment.

The flake follows a source revision, not GitHub's prebuilt binary release channel. Pin the input revision for reproducible deployments. Nix-built cloud clients default to mutable latest* container images because a Cargo package version is not evidence that a corresponding stable image was published; only official stable release builds embed a release version and resolve its exact published image digest.

Flake Setup

Add mold to your flake inputs and import the module:

nix
{
  inputs.mold.url = "github:utensils/mold";

  outputs = { self, nixpkgs, mold, ... }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      modules = [
        mold.nixosModules.default
        ./mold.nix  # your mold config (see below)
      ];
    };
  };
}

Minimal Configuration

nix
{ inputs, system, ... }:
{
  services.mold = {
    enable = true;
    package = inputs.mold.packages.${system}.default;  # Ada / RTX 40-series
  };
}

This starts mold serve on port 7680 with sensible defaults, creates a mold system user, and manages the data directory at /var/lib/mold.

Web gallery is bundled

Since v0.8.1 the Vue 3 gallery SPA is embedded directly into the mold binary at compile time — visiting http://<host>:7680/ opens the gallery with no extra configuration. Earlier versions required staging web/dist/ into ~/.mold/web or pointing MOLD_WEB_DIR at a built SPA. That override still works for SPA hot-iteration without recompiling Rust.

Full Configuration Example

nix
{ inputs, system, config, ... }:
{
  services.mold = {
    enable = true;

    # Package — must match your GPU architecture
    package = inputs.mold.packages.${system}.default;     # Ada (RTX 4090, sm_89)
    # package = inputs.mold.packages.${system}.mold-sm86;  # RTX 3090/A40, sm_86
    # package = inputs.mold.packages.${system}.mold-sm100; # B200/B300, sm_100
    # package = inputs.mold.packages.${system}.mold-sm120; # RTX 5090, sm_120

    # Advisory hint — emits a build warning if package doesn't match
    # cudaArch = "blackwell";

    # Server
    port = 7680;
    bindAddress = "0.0.0.0";
    logLevel = "info";         # trace, debug, info, warn, error
    openFirewall = false;      # set true to allow LAN access
    # mdns = true;             # advertise + browse _mold._tcp (set false for MOLD_MDNS=0)

    # Directories
    homeDir = "/var/lib/mold";           # MOLD_HOME
    # modelsDir = "/var/lib/mold/models"; # defaults to homeDir/models

    # Models
    defaultModel = "flux2-klein:q8";

    # Multi-GPU — pin the server to specific cards (null = use all visible)
    # gpus = "0,1";
    # queueSize = 200; # max queued jobs; overflow returns HTTP 503

    # Image persistence — save copies of all server-generated images
    # outputDir = "/srv/mold/gallery";

    # CORS — restrict to specific origin (null = permissive)
    # corsOrigin = "https://mysite.example.com";

    # Catalog auth defaults — users can override these in web Settings
    # Points to files containing tokens (e.g. agenix secrets)
    hfTokenFile = config.age.secrets.hf-token.path;
    civitaiTokenFile = config.age.secrets.civitai-token.path;

    # API key authentication — file with one key per line (e.g. agenix secret)
    # When set, all API requests require an X-Api-Key header
    # apiKeyFile = config.age.secrets.mold-api-key.path;

    # Rate limiting — per-IP, generation endpoints at configured rate, reads at 10x
    # rateLimit = "10/min";
    # rateLimitBurst = 20;

    # Extra environment variables
    environment = {
      MOLD_EAGER = "1";        # keep all components loaded
      MOLD_T5_VARIANT = "q4";  # use Q4 T5 encoder
      # MOLD_THUMBNAIL_WARMUP = "1"; # opt in to startup gallery thumbnail warmup
    };

    # Discord bot
    discord = {
      enable = true;
      # Must be an EnvironmentFile: MOLD_DISCORD_TOKEN=your-token-here
      tokenFile = config.age.secrets.discord-token.path;
      # moldHost = "http://localhost:7680";  # defaults to main server
      cooldownSeconds = 10;
      # allowedRoles = "artist, 1234567890";  # restrict to specific roles
      # dailyQuota = 20;                       # max generations per user per day
      logLevel = "info";
    };
  };
}

Module Options Reference

Server Options

OptionTypeDefaultDescription
enableboolfalseEnable the mold server
packagepackageThe mold package (must set explicitly)
cudaArchnull/enumnullSee the exact advisory architecture-to-package mapping below
portport7680HTTP server port
bindAddressstring"0.0.0.0"Address to bind
homeDirstring"/var/lib/mold"Base directory (MOLD_HOME)
modelsDirstringhomeDir + /modelsModel storage directory
logLevelenum"info"Log level (trace/debug/info/warn/error)
corsOriginnull/stringnullCORS origin restriction (null = permissive)
openFirewallboolfalseOpen firewall port (also UDP 5353 when mdns is on)
mdnsbooltrueAdvertise and browse _mold._tcp; false sets MOLD_MDNS=0
defaultModelnull/stringnullDefault model name
gpusnull/stringnullall, none, ordinals, or stable CUDA/Metal/NVIDIA UUID IDs
queueSizenull/intnullMax queued generation jobs (null = default 200)
outputDirnull/stringnullImage output directory (default: homeDir/output)
hfTokenFilenull/pathnullPath to overridable default HuggingFace token
civitaiTokenFilenull/pathnullPath to overridable default Civitai token
apiKeyFilenull/pathnullPath to file with API key(s) for authentication (e.g. agenix secret)
rateLimitnull/stringnullPer-IP rate limit (e.g. "10/min")
rateLimitBurstnull/intnullOverride burst allowance (defaults to 2x rate)
logToFileboolfalseEnable file logging (in addition to journal)
logDirstringhomeDir + /logsDirectory for log files when logToFile is enabled
logRetentionDaysint7Days to retain rotated log files
environmentattrs{}Extra environment variables

cudaArch does not select a package automatically. Set package to the matching flake output:

  • "ampere"packages.${system}.mold-sm86 (RTX 3090/A40, sm_86)
  • "ada"packages.${system}.mold (RTX 40-series, sm_89)
  • "blackwell-datacenter"packages.${system}.mold-sm100 (B200/B300, sm_100)
  • "blackwell"packages.${system}.mold-sm120 (RTX 50-series, sm_120)

Monitoring

Nix builds include the metrics feature. The server exposes GET /metrics in Prometheus text exposition format (HTTP request rates, generation duration, queue depth, GPU memory, uptime). The endpoint is excluded from auth and rate limiting, so Prometheus/Grafana Agent can scrape it without an API key.

Discord Bot Options

OptionTypeDefaultDescription
discord.enableboolfalseEnable Discord bot service
discord.packagepackageconfig.services.mold.packagePackage for the bot
discord.tokenFilepathFile containing bot token
discord.moldHoststring"http://localhost:{port}"mold server URL
discord.cooldownSecondsint10Per-user generation cooldown
discord.allowedRolesstring?nullComma-separated role names/IDs (null = all)
discord.dailyQuotaint?nullMax generations per user per day (null = unlimited)
discord.logLevelenum"info"Bot log level
discord.environmentattrs{}Extra environment variables for the Discord bot

What the Module Creates

  • System user mold:mold with home at homeDir
  • Directories via tmpfiles: homeDir, modelsDir, and outputDir (if set)
  • Systemd service mold.service — runs mold serve with:
    • video and render supplementary groups for GPU access
    • Hardened: NoNewPrivileges, ProtectSystem=full, ProtectHome, PrivateTmp
    • HuggingFace token loaded via EnvironmentFile (never in process env)
  • Systemd service mold-discord.service (if discord.enable) — runs mold discord, depends on mold.service, further hardened with ProtectSystem=strict and PrivateDevices (no GPU needed)
  • Firewall rule if openFirewall = true

GPU Architecture

The module cannot auto-select the flake package — you must set package to match your GPU:

GPUPackage
RTX 3090 / A40 (Ampere)inputs.mold.packages.${system}.mold-sm86
RTX 40-series (Ada)inputs.mold.packages.${system}.mold
B200 / B300 (datacenter Blackwell)inputs.mold.packages.${system}.mold-sm100
RTX 50-series (consumer Blackwell)inputs.mold.packages.${system}.mold-sm120

Set cudaArch to the matching ampere, ada, blackwell-datacenter, or blackwell value. This is an advisory consistency check: it emits a build warning if the package's Mold CUDA capability metadata does not match, but never switches the package itself. All four official package variants carry that metadata; custom packages without it warn rather than being assumed safe.

Build Variants

bash
nix build github:utensils/mold
bash
nix build github:utensils/mold#mold-sm86
bash
nix build github:utensils/mold#mold-sm100
bash
nix build github:utensils/mold#mold-sm120

B200/B300 support is simulated, not hardware-qualified. Hosted release CI builds the sm_100 server package alongside sm_86 and the sm_86 desktop output; real B200 qualification remains deferred. There is intentionally no sm_100 desktop package. GH200, GB200, and GB300 require future linux/arm64 artifacts and are unsupported. The current Linux flake outputs are x86_64 and must not be selected for Grace systems.

Development Shell

bash
nix develop github:utensils/mold

The devshell includes Rust toolchain, CUDA toolkit, and convenience commands:

CommandDescription
buildFast local mold build (dev-fast) with embedded web UI
build-workspacecargo build (debug, all crates)
build-releaseShipping release build with the full feature set
build-serverFast local server build with GPU + preview + expand
serveStart the mold server
generateGenerate an image
moldRun any mold CLI command
checkcargo check
clippycargo clippy
fmtcargo fmt
run-testscargo test
coverageTest coverage report
docs-devStart VitePress docs dev server
docs-buildBuild the documentation site
docs-fmtFormat docs with prettier