Jump to
↵select↑↓navigateescclose

Reference

Dev shell options

Every perSystem.frappe-nix option that configures the development shell, the production package and the container images, with types and defaults.

Updated
Tags
  • options
  • reference
  • devenv
On this page

These options sit under perSystem.frappe-nix in your flake. The types and defaults were checked against the module by evaluating it. The prose describes what each option does. For a guided tour of the options you will touch most, see Write the flake by hand. The options under frappe-nix.secrets sit at the top level and have their own page, and the production module's options are under services.frappe-nix.

Every TCP port (ports.http, ports.db, ports.smtp, ports.mailUi, ports.pop3 and the rest) is a base plus ports.offset, an offset between 0 and 899 hashed from name. That is the preferred port. devenv up leases nginx's and Mailpit's before it starts anything: a free preferred port is the port, and a taken one is moved past in app mode or refused in bench mode (ports.strict). frappe-nix-ports prints the ports in force. In app mode FRAPPE_NIX_PORT_OFFSET picks another offset for the shell.

Core

The options every bench sets.

OptionTypeDefaultNotes
enableboolfalseEnable the dev shell and the packages.
namestrrequired (the normalized app.name in app mode)The project's identity, a slug. It seeds the per-bench port offsets and names the container images, the dev processes' systemd scope and the shell's messages; devenv's own name defaults to it. Formerly benchName.
defaultSitestr""Frappe's default_site, and the shell's FRAPPE_SITE. Empty means multi-tenancy, with the site set per shell through .env. Formerly siteName.
workspaceRootpath or nullnullThe bench root, where pyproject.toml and apps/ live, usually ./.. Required in bench mode. It must stay null in app mode, where frappe-nix assembles the workspace itself.
pythonpackagepkgs.python312Python interpreter. In app mode, the app.frappeVersion preset's.
nodejspackagepkgs.nodejs_22Node.js for frontend builds and the Node realtime server. In app mode, the app.frappeVersion preset's.
esbuildTargetstr"es2022"The target Frappe's esbuild pipeline compiles bundles for, exported as ESBUILD_TARGET in the dev shell and to builtBench. See Asset builds.

App mode

Set app.enable = true in the repository of a single Frappe app. See Develop a single app. workspaceRoot must stay null there.

OptionTypeDefaultNotes
app.enableboolfalseApp mode: this flake is one Frappe app's repository, not a bench.
app.frappepath(required in app mode)The Frappe source, as a flake = false input.
app.siblingslist of { name; src; }[ ]The other apps the bench should carry, in install order. It is a list and not an attribute set, because that order is the members' order and so the order of sites/apps.txt. name is the directory name under apps/, and src is the source, usually a flake input.
app.frappeVersiondevelop, version-15 or version-16"version-16"The row of lib/frappe-presets.json that drives python, nodejs, requires-python and override-dependencies. It does not pin Frappe: app.frappe does.
app.srcpathinputs.selfThe app's own source, which becomes apps/<NAME>. Do not filter it: app.lockDir is read out of it.
app.namestr[project].name of app.srcThe app's directory name under apps/, which is Frappe's own app name.
app.lockDirstr"nix"Where the generated-but-committed uv.lock (and node-locks/, for pins without a yarn.lock) live, relative to the repo root.
app.benchDirstr".frappe-nix/bench"Where the dev shell materializes the writable bench, relative to the repository root. Gitignore it.
app.excludeNodeTargetslist of str[ ]Top-level directories of the app that are never node targets: no node_modules, no fallback lock, no build. An opted-in app's managed flake.nix sets [ "docs-site" ] while the docs-site module is on. See App assets.

The watcher

How the watch process chooses what to rebuild. See Memory and disk.

OptionTypeDefaultNotes
watch.appslist of str or nullnullApps the watch process rebuilds on save. null chooses by publisher (watch.excludePublishers). See Memory and disk.
watch.excludePublisherslist of str[ "Frappe Technologies" ]With watch.apps null, leave out every app whose hooks.py app_publisher contains one of these, ignoring case. [ ] watches everything, as bench watch does.
watch.rtlboolfalseAlso rebuild right-to-left stylesheets on save. bench build always builds them.
watch.nativeSassbooltrue where nixpkgs builds dart-sassCompile the watcher's stylesheets with native Dart Sass (lib/sass-embedded.nix) instead of Frappe's JavaScript build of it, 3 to 6 times faster per stylesheet. bench build keeps Frappe's compiler.
watch.pythonbooltrueRestart the server on a Python source change: the runtime's --dev, or bench serve's reloader with runtime.enable = false. /assets and /files are served either way. Formerly runtime.dev.
watch.reassert.hookslist of str[ ]bench execute targets run when sites/assets/assets.json names a bundle file that does not exist on disk. Empty by default, so it names no app. Needs defaultSite. See Assets. Formerly assets.reassert.hooks.
watch.reassert.debounceMsint750How long assets.json must sit unmodified before the bench-watch-driven check re-reads it. Formerly assets.reassert.debounceMs.

MariaDB

OptionTypeDefaultNotes
mariadb.packagepackagepkgs.mariadbMariaDB package.
mariadb.initialDatabaseslist of { name }[ ]Databases created on the first devenv up.
mariadb.durableboolfalseFlush InnoDB to disk at every commit and keep the doublewrite buffer, as production does. Off, the log is flushed once a second, which makes commits roughly 30 times faster, at the risk of the last second of commits if the machine crashes. See Memory and disk.
mariadb.noCowbooltrueOn btrfs, keep the data directory off copy-on-write (chattr +C). Set at shell entry on a directory that is missing or empty. One already holding data is reported, and frappe-nix-db-nocow migrate "$MYSQL_HOME" moves it once.

Process scope

Runs devenv up in a systemd scope of its own. See Memory and disk.

OptionTypeDefaultNotes
processScope.enablebooltrueRun devenv up in a systemd scope of its own, so the dev stack is not accounted to, and killed along with, the editor that started it. Linux with a systemd user manager and the process-compose manager; a no-op elsewhere.
processScope.memoryHighstr"40%"The scope's MemoryHigh=: past it the dev stack is throttled and reclaimed from first. A percentage is of physical RAM.
processScope.memoryMaxstr or nullnullThe scope's MemoryMax=, or none.

Node builds

OptionTypeDefaultNotes
nodeOverridesattrs of attrs{ }Per node target (an app, or app/subdir): extra attributes for the derivation that runs its yarn install --offline, such as postPatch, nativeBuildInputs or a yarnOfflineCache of your own. See Dependencies and locks.
nodeNestedFrontendExcludeslist of str[ ]Nested frontends (app/subdir) to leave out: no node_modules, no assets, and the parent's build script that drives them is dropped. See Dependencies and locks.

Packages, scripts and environment

OptionTypeDefaultNotes
pythonOverridesoverlayno-op overlayExtra Python package set overlay, on top of the shared fixes and mysqlclient (see lib.overrides).
pythonBuildDependenciesattrs of lists{ }This bench's own [tool.uv.extra-build-dependencies] entries, such as { fuuid = [ "poetry-core" ]; }, in place of the ones derived from uv.lock. See Build dependencies.
extraDevPackageslist of package[ ]Extra packages on the dev shell.
extraContainerRuntimeDepslist of package[ ]Extra runtime packages in production containers.
extraPackageslist of package[ ]Extra packages installed in both the dev shell and any production deployment of this package. The NixOS module reads them off builtBench's passthru.extraPackages, so no server-side configuration is needed.
extraLibraryPathslist of package[ ]Extra LD_LIBRARY_PATH entries (dev shell).
extraScriptsattrs{ }Extra devenv scripts, merged over the standard set.
extraEnvattrs of str{ }Extra environment variables (dev shell).

Runtime

The unified frappe-runtime process. See The unified runtime. The production counterparts are under services.frappe-nix.runtime.

OptionTypeDefaultNotes
runtime.enablebooltrueRun one frappe-runtime process (web, realtime, jobs and scheduler) instead of the split web, socketio, worker and scheduler processes.
runtime.jobThreadsint2Concurrent background jobs inside the runtime process.
runtime.srcnull or pathruntime/ in this repositoryThe source frappe-runtime is built from, overriding the revision uv.lock resolved. null hands control back to uv.lock.

Ports and sockets

See The development shell.

OptionTypeDefaultNotes
sockets.enablebooltruePut MariaDB, Redis, the realtime server and the web server on unix sockets behind one nginx port, so several benches can run at once. Needs Frappe 15.46 or newer.
ports.offsetint, 0–899a hash of nameThe offset every preferred TCP port derives from: web 8000 +, MariaDB 3306 +, Mailpit 19000, 20000 and 21000 +. In app mode with ports.worktreeSalt, a linked worktree hashes <name>@<path>. In app mode FRAPPE_NIX_PORT_OFFSET wins over it.
ports.worktreeSaltboolwhether the app opted in to the app standardsApp mode: salt a linked worktree's ports.offset with its path, so two worktrees of one app prefer different ports. On by default only with a [tool.frappe-nix] table; any app may set it. Bench mode ignores it.
ports.baseport or nullnullThe web port, overriding 8000 + ports.offset. Mailpit and MariaDB keep theirs. While ports.strict is unset, devenv up refuses it when it is taken rather than moving it.
ports.strictbool or nullnull: strict in bench mode, not in app modeWhether devenv up refuses a taken preferred port, naming the bench or process that holds it, instead of leasing the next free one. Bench mode is always strict, because its web port is committed: false there is an evaluation error. FRAPPE_NIX_STRICT_PORTS=1 makes any devenv up strict and =0 lets an app-mode bench move, read when devenv up runs. With sockets.enable = false the ports are always strict.
ports.spanint, 1–1000100How many ports from the preferred one devenv up tries for each service before it gives up.
ports.httpportports.base, else 8000 + ports.offsetThe web port: nginx, or the server itself without sockets. Each ports.* below is derived for the module to read; set one (lib.mkForce) only to fix that service's port.
ports.dbport3306 + ports.offsetMariaDB's port. Unbound in socket mode.
ports.socketioport9000 + ports.offsetThe Node realtime server's, with runtime.enable = false and no sockets.
ports.smtpport19000 + ports.offsetMailpit's SMTP port: mailpit.port's default.
ports.mailUiport20000 + ports.offsetMailpit's web UI: mailpit.httpPort's default.
ports.pop3port21000 + ports.offsetMailpit's POP3 port: mailpit.pop3.port's default.

The ports.* and mailpit.* port values are what the shell evaluates: the preferred ports. The ports a running bench serves on are in $DEVENV_RUNTIME/frappe-nix/ports.json (also $FRAPPE_NIX_PORTS_FILE), written by the frappe-ports process when devenv up starts and removed when the bench stops:

{ "bench_id": "my-app/devenv-1a2b3c4", "name": "my-app", "root": "/src/my-app", "pid": 41234,
  "web": 8298, "mailpit_smtp": 19297, "mailpit_ui": 20297,
  "preferred": { "web": 8297, "mailpit_smtp": 19297, "mailpit_ui": 20297 }, "moved": { "web": 8297 } }

frappe-nix-ports reads it: with no argument it lists the ports, frappe-nix-ports web prints one, --json the file, and who <port> names the bench (or "a process outside frappe-nix") holding a port. When the bench is not up it prints the preferred ports and exits 3.

Apps and assets

OptionTypeDefaultNotes
appsReconcile.enablebooldefaultSite != ""Install whatever sites/apps.txt names that defaultSite's site does not have installed yet, on every devenv up. See Apps in a bench.
renamedAppsattrs of str{ }Apps renamed in place, OLD = NEW: reconcile-apps runs frappe-rename-app --site before it installs anything. See Renaming an app.
replacedAppsattrs of str{ }Apps replaced by a new app, OLD = NEW: reconcile-apps installs NEW and uninstalls OLD on a site that has OLD. See Renaming an app.

Mailpit

The mail catcher, as a service. It runs whether or not the mail guard is on, and every process that can send mail starts after it. With the guard on (devguard.mail.enable), every site's outgoing mail goes to it.

OptionTypeDefaultNotes
mailpit.enablebooltrueRun Mailpit with the bench's processes. Before, it ran only with the mail guard on. Off with the guard on, mail goes to a port nothing listens on.
mailpit.hoststr"127.0.0.1"Interface Mailpit binds and the mail guard redirects to. Formerly devguard.mail.host.
mailpit.portportports.smtpSMTP port, and where the mail guard sends mail. Formerly devguard.mail.port (devguard.mail.smtpPort before that).
mailpit.httpPortportports.mailUiWeb UI port. Formerly devguard.mail.httpPort.
mailpit.pop3.enableboolfalseServe incoming mail from Mailpit's POP3 listener, and have the mail guard send Frappe's POP3 there instead of refusing it. Formerly devguard.mail.pop3.enable.
mailpit.pop3.portportports.pop3POP3 port. Formerly devguard.mail.pop3.port.
mailpit.pop3.userstr"dev"POP3 username (local development only). Formerly devguard.mail.pop3.user.
mailpit.pop3.passwordstr"dev"POP3 password (local development only). Formerly devguard.mail.pop3.password.

Development guard rails

Each guard is independently switchable, and devguard.enable = false turns them all off. See Development guard rails.

OptionTypeDefaultNotes
devguard.enablebooltrueMaster switch for all guard rails. See Development guard rails.
devguard.egress.enablebooltrueRefuse every connection the bench's Python opens to anything but this machine.
devguard.egress.allowHostslist of str[ ]Hostnames (*. for subdomains), addresses or CIDRs to let through. Shared with integrations.allowHosts.
devguard.mail.enablebooltrueRoute all outgoing mail to Mailpit (mailpit.host:mailpit.port), and refuse IMAP and POP3, or serve them from Mailpit with mailpit.pop3.enable. Mailpit itself is the mailpit tree below.
devguard.mail.senderstr"notifications@example.com"From address used only on sites with no outgoing Email Account at all.
devguard.mail.unmutebooltrueIgnore mute_emails in site_config.json.
devguard.backups.enablebooltrueBlock Dropbox, S3, Google Drive and Frappe Cloud backup upload.
devguard.objectstore.enablebooltrueNever delete from, or overwrite in, the configured S3 bucket.
devguard.objectstore.modelocal or push"local"local: cloud_storage writes to local disk. push: new files are uploaded to the bucket (additive only).
devguard.objectstore.conditionalWritesbooltrueSend If-None-Match: * on push-mode writes. Set false for Backblaze B2, which answers it with 501.
devguard.integrations.enablebooltrueBlock outbound HTTP via frappe.integrations.utils.make_request.
devguard.integrations.allowHostslist of str[ ]Hosts to permit anyway. Loopback is always allowed. Shared with egress.allowHosts.
devguard.google.enablebooltrueBlock Google Calendar, Contacts and Drive access.
devguard.webhooks.enablebooltrueDrop outbound Webhook requests.
devguard.plaid.enablebooltrueBlock Plaid bank synchronization.
devguard.jobs.enablebooltrueSkip scheduled jobs that reach production services. Formerly devguard.scheduler.enable.
devguard.jobs.blockServerScriptsbooltrueSkip Scheduled Job Types backed by a Server Script. Formerly devguard.scheduler.blockServerScripts.
devguard.jobs.extraBlockedlist of str[ ]Extra Scheduled Job Type.method values to skip (exact match). Formerly devguard.scheduler.extraBlockedJobs.

Restore

How bench restore fetches a production backup. See Restore a production backup.

OptionTypeDefaultNotes
restore.enableboolfrappe-nix.secrets.backupAccess.enableLet bench restore fetch from the object store. See Restore a production backup.
restore.prefixstr""Path inside the bucket. Normally carried in the secret as BACKUPS_PREFIX instead.
restore.filesnone, private, all, or a bool"none"File archives to pull by default. They are routinely tens of GB. Formerly restore.withFiles.
restore.neutralizebooltruePause the scheduler and switch mail off in a database fetched from the object store.
restore.carryConfigKeyslist of str[ "encryption_key" "backup_encryption_key" ]Allowlist of keys copied from the backup's site config.
restore.migratebooltrueRun bench migrate after restoring.
restore.requireDevguardbooltrueRefuse to restore into an unguarded bench (FRAPPE_NIX_RESTORE_ALLOW_UNGUARDED=1 overrides once).

Offline migrate

Alter large tables online in front of bench migrate. See Migrate large tables online.

OptionTypeDefaultNotes
offlineMigrate.enableboolfalseRun bench-offline-migrate in front of every bench migrate, so the ALTERs of large tables cannot lock them. Adds percona-toolkit to the closure.
offlineMigrate.rowThresholdint100000Rows at which a table is altered online. 0 sends every table with a pending change through pt-online-schema-change. Overridable with FRAPPE_NIX_OFFLINE_MIGRATE_ROW_THRESHOLD or --threshold.

Containers

See Build production images.

OptionTypeDefaultNotes
containers.enableboolfalseBuild the OCI images.
containers.registrystr""Registry prefix for the image names: "ghcr.io/avunu" names them ghcr.io/avunu/<name>/<role>. A trailing slash is dropped. frappe-nix does not push. See Build production images.