Graphical editor for nix configurations. Zero dependencies, no build step, no framework — just Python and a browser.
Blog post: https://kalken.github.io//ezblog/#ezconf
✨ Features
- Edit NixOS configuration through a clean web UI with option autocomplete
- Split config across multiple files and folders — organize however you like, merged only at Nix-eval time
- Inline terminal panel with configurable shortcut buttons
- PAM auth (system credentials) or custom username/password
- HTTPS with automatic local CA generation and browser trust store installation
- Three themes: NixOS blue, dark, light
🚀 Quick Start
Add to your flake inputs:
inputs.ezconf.url = "github:kalken/ezconf"; inputs.ezconf.inputs.nixpkgs.follows = "nixpkgs";
Add the module import to your flake.nix's module list — it defines the services.ezconf.* options, so it needs to live somewhere that survives migrating away from configuration.nix (see below), unlike the option values themselves:
nixosConfigurations.myhostname = nixpkgs.lib.nixosSystem { modules = [ inputs.ezconf.nixosModules.default ./configuration.nix ./ezconf # created automatically on first start ]; };
Then enable the service in your NixOS configuration (e.g. configuration.nix), same as any other option:
{ ... }: { services.ezconf = { enable = true; auth.allowedUsers = [ "alice" ]; buttons = [ { label = "Rebuild"; command = "nixos-rebuild switch --flake /etc/nixos"; save_first = true; } ]; }; }
After nixos-rebuild switch the editor is at https://localhost:9090. A local CA and certificate are generated automatically, and installed into the browser trust store for each user in allowedUsers.
Tip: In Chrome or any Chromium-based browser, open the address bar menu and choose Install page as app to get a standalone desktop app with no browser chrome.
🔁 Migrating from configuration.nix
- Enable the service and rebuild — this creates
/etc/nixos/ezconf/(empty; nothing is seeded automatically) - Open the editor and use the import button to import your existing
configuration.nix— the "Import into" field accepts any name (e.g.configuration.json) and creates that file for you in one step - In your
flake.nix, comment out./configuration.nixin the modules list —./ezconfand theezconf.nixosModules.defaultimport are already there from Quick Start and don't depend on it - Rebuild — your config is now managed through the editor
Note: Any
importsyou had inconfiguration.nixshould be moved to yourflake.nixafter migrating — the JSON-based config does not supportimports.
📑 Multiple Config Files
Configuration doesn't have to live in one big configuration.json — split it across as many *.json files as you like, each one an independent tab in the header with its own save/undo/backup history. Files are only combined at Nix-eval time (via lib.mkMerge), the same as splitting a hand-written configuration.nix across modules: lists and attribute sets merge normally, and a scalar option set differently in two files is a plain Nix eval error, not something ezconf tries to resolve for you. Group files into folders for your own organization (e.g. services/nginx.json) — folders are just cosmetic grouping in the tab bar, not part of the Nix module structure.
There's no "+" button anywhere — file and folder management is entirely right-click:
- Right-click empty space in the tab bar (or the empty editor area, if there are no files yet) for New file / New folder.
- Right-click a folder for New file here or Delete folder (removes everything inside it).
- Right-click a tab for Delete.
- Double-click a tab to rename it inline.
- Drag a tab into a folder (or back out to the root) to move it.
- Drag a section or option onto a different file's tab to move it there, or use Copy / Cut / Paste (also right-click) to duplicate or relocate a section or option to the same path in another file.
A fresh install starts with zero files — the empty editor area explains how to create the first one, and the Import modal's "Import into" field can create a new file on the spot.
🖥️ Standalone
git clone https://github.com/kalken/ezconf
cd ezconf
python3 bin/server.py --file /path/to/configuration.jsonOr with a config file:
cp example/ezconf.example.toml ezconf.toml $EDITOR ezconf.toml python3 bin/server.py # Point at a config file in another location python3 bin/server.py --config /path/to/ezconf.toml
Open http://localhost:9090. Authentication is always required — without PAM available it falls back to custom mode (set username and password in ezconf.toml).
Optional Python dependencies: python-pam (PAM auth), cryptography (--generate-cert), tomli (TOML config on Python < 3.11).
Nix Expressions
Right-click any field for Convert to Nix expression, which switches it to raw Nix expression mode. In this mode you can type any valid Nix expression directly — useful for freeform options that don't map cleanly to a structured form, such as Samba shares or extraConfig strings.
Right-click the field again for Convert to [type] to turn it back into its native type.
Disable Toggle
Right-click any option or section for Disable. Disabled options remain fully visible in the editor (dimmed with the key struck through) but are excluded from the Nix configuration at evaluation time — the value is preserved and can be re-enabled at any time via the same menu's Re-enable item.
Disabling a section disables all of its children at once.
🔐 Authentication
Three modes, set via auth.method:
auto— PAM if available, else custom (default)pam— system username + password viapython-pamcustom— username/password from config
PAM mode with allowed users:
services.ezconf = { enable = true; auth.method = "pam"; auth.allowedUsers = [ "alice" "bob" ]; };
Custom credentials:
services.ezconf = { enable = true; auth.method = "custom"; auth.username = "admin"; auth.passwordFile = "/var/lib/ezconf/password"; };
Create the password file once:
echo -n "mypassword" > /var/lib/ezconf/password chmod 600 /var/lib/ezconf/password
Note: The username must be set in your NixOS config. For the password, prefer
auth.passwordFileoverauth.password— the latter is stored in the Nix store and world-readable. The runtime config at/run/ezconf/ezconf.tomlis regenerated on every service start, so editing it directly has no effect.For standalone use, you can set
usernameandpassworddirectly inezconf.tomland they will be picked up on the next start.
🖱️ Terminal Panel
The terminal panel runs as a separate service (ezconf-terminal.service) and is enabled by default. Configure shortcut buttons to run common commands:
services.ezconf = { enable = true; terminal = true; buttons = [ { label = "Rebuild"; command = "nixos-rebuild switch --flake /etc/nixos"; save_first = true; } { label = "Update"; command = "nix flake update /etc/nixos"; } { label = "Check"; command = "nix flake check /etc/nixos"; } ]; };
save_first = true disables the button while there are unsaved changes. The terminal service has restartIfChanged = false so active sessions survive nixos-rebuild switch.
If you're editing multiple config files (tabs), buttons show up regardless of which tab is active by default. Set always_show = false on a button to only show it while its own defining file is the active tab — handy for a shortcut that only makes sense in the context of one specific file.
🔒 HTTPS
HTTPS is enabled by default. When no cert or key are provided a local CA and certificate are generated automatically in /var/lib/ezconf/. With installCerts = true (the default) the CA is installed into ~/.pki/nssdb for each user in auth.allowedUsers so browsers trust it without a warning.
The login page shows a Download CA certificate link when a generated CA is available — use this to import the CA into browsers or devices that aren't covered by installCerts (e.g. macOS or other machines on the network). The CA is stable and never regenerated unless deleted, so this is a one-time import. The server cert is regenerated automatically when listen or certNames change, with no browser action needed.
To use your own certificate:
services.ezconf = { enable = true; https = true; cert = "/path/to/cert.pem"; key = "/path/to/key.pem"; };
For dev use without the NixOS module:
# Local CA + cert (install localhost-ca.pem in your browser once) python3 bin/server.py --generate-ca # Or self-signed (browser will warn) python3 bin/server.py --generate-cert
🌐 Accessing from other devices
To reach ezconf from other devices on your network, set listen to a LAN IP or 0.0.0.0 for all interfaces. The firewall is opened and a TLS certificate covering the listen address is generated automatically. Set interface to restrict the firewall rule to a specific network interface instead of opening the port on all interfaces:
services.ezconf = { enable = true; listen = "192.168.1.2"; interface = "enp3s0"; # optional: restrict firewall rule to this interface auth.allowedUsers = [ "alice" ]; };
The editor is then reachable at https://192.168.1.2:9090 from any device on the network.
Trusting the certificate on other devices: the login page shows a Download CA certificate link. Download ezconf-ca.pem and import it once on each device:
- macOS: open the file in Keychain Access → set trust to Always Trust
- Windows: double-click → Install Certificate → Local Machine → Trusted Root Certification Authorities
- Firefox (any OS): Settings → Privacy & Security → View Certificates → Authorities → Import
- Android: Settings → Security → Install from storage
The CA never changes, so this is a one-time step per device.
If you access ezconf by hostname rather than IP, add the hostname to certNames so the certificate covers it:
services.ezconf = { enable = true; listen = "192.168.1.2"; certNames = [ "myserver.local" ]; };
🔄 Autocomplete Data
The editor loads NixOS option, package, and kernel data from autocomplete_dir if set in the config, otherwise autocomplete/ under the webroot. The NixOS module sets autocomplete_dir to /var/lib/ezconf/autocomplete/ and generates the data on first start. To regenerate from the UI, the ↻ Autocomplete button appears automatically when mkoptions is configured (the module sets this up).
For standalone use:
# Writes to webroot/autocomplete/ by default nix run .#ezconf-mkoptions # Against a specific flake + hostname TARGET=/path/to/flake nix run .#ezconf-mkoptions -- all myhostname
💾 Backups
Every time a config file is saved, the server copies its previous contents into a backup directory, keeping the backup_count most recent copies per file (default 5; set to 0 to disable). Backups are per-tab — with multiple config files, each gets its own independent history. The 🕐 Backups button appears in the header automatically once backups are enabled — it lists past saves of whichever tab is currently active, with their timestamp and size, and lets you restore any of them with one click. Restoring overwrites that file directly and does not itself create a backup. You can also back up a file's current on-disk contents on demand at any time, without waiting for a save.
Standalone: set backup_dir / backup_count in ezconf.toml, or pass --backup-dir / --backup-count. Backups default to a shared .ezconf-backups/ directory inside the config directory (one subset per file). The NixOS module stores them in /var/lib/ezconf/backups by default (backupDir / backupCount options).
🎨 Theme
services.ezconf = { enable = true; theme = "dark"; # nixos (default) | dark | light };
⚙️ NixOS Module Options
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable ezconf |
user / group |
str | "root" |
User and group for the services |
configDir |
str | "/etc/nixos/ezconf" |
Directory for the *.json tabs and default.nix; starts empty — create your first file in the editor |
defaultFile |
str | "configuration.json" |
File (relative to configDir) preselected in the editor when a browser has no prior tab remembered; a hint only, nothing creates it automatically |
webroot |
str | "${package}/share/ezconf" |
Directory to serve static assets from |
auth.method |
str | "auto" |
auto, pam, or custom |
auth.username |
str or null | null |
Username for custom auth |
auth.password |
str or null | null |
Password for custom auth (stored in Nix store — prefer passwordFile) |
auth.passwordFile |
path or null | null |
File containing the password for custom auth |
auth.allowedUsers |
list of str | [] |
Users allowed to log in (PAM mode); defaults to the service user |
theme |
str | "nixos" |
nixos, dark, or light |
terminal |
bool | true |
Enable terminal panel and ezconf-terminal.service |
shell |
str or null | null |
Shell for the terminal (defaults to the login shell of user) |
buttons |
list | [] |
Shortcut buttons shown in the terminal panel |
https |
bool | true |
Enable HTTPS |
generateCert |
bool | auto | Generate a local CA + cert in /var/lib/ezconf/ (set automatically when https = true and no cert/key provided) |
certNames |
list of str | [] |
Extra hostnames or IPs to include in the generated cert (e.g. [ "myserver.local" ]); localhost, 127.0.0.1, and listen are always included |
installCerts |
bool | true |
Install generated CA into ~/.pki/nssdb for each user in allowedUsers |
cert |
str or null | null |
Path to TLS certificate (PEM) |
key |
str or null | null |
Path to TLS private key (PEM) |
listen |
str or null | null |
IP address to listen on (default: 127.0.0.1; use 0.0.0.0 for all interfaces) |
openFirewall |
bool | false |
Open firewall ports for the web and terminal services; enabled automatically when listen is set to a non-localhost address |
interface |
str or null | null |
Network interface to open firewall ports on (e.g. "eth0"); when set, ports are opened only on that interface instead of all interfaces |
trustedHosts |
list of str | [] |
Extra hostnames trusted for CSRF check — required when behind a reverse proxy; listen and certNames are trusted automatically |
nixosTarget |
str | "/etc/nixos" |
Flake path passed to ezconf-mkoptions |
backupDir |
str | "/var/lib/ezconf/backups" |
Directory to store config file backups (one subset per file) |
backupCount |
int | 5 |
Number of backups to keep, made on every save; 0 disables backups |
ports.web |
port | 9090 |
Web server port |
ports.terminal |
port | 9091 |
Terminal WebSocket port |
📝 Notes
configDiris created automatically with adefault.nixthat applies whatever*.jsonfiles end up there — it starts with none; create your first one from the editor (right-click the tab bar, or the empty editor area, for "New file"). Add./ezconfto yournixosSystemmodules list inflake.nixto wire it in.- Autocomplete data is generated on first service start into
/var/lib/ezconf/autocomplete/and can be refreshed from the UI. - The terminal service has
restartIfChanged = false— active terminal sessions survivenixos-rebuild switch. auth.passwordis stored in the Nix store (world-readable). Useauth.passwordFilefor anything real.- PAM mode defaults
allowedUsersto the user running the service if the list is empty. - The editor always requires authentication — there is no unauthenticated mode.
- The service runs as
rootby default. This is intentional — it allows the terminal panel to runnixos-rebuildand other system commands without additional privilege escalation.
Edit your NixOS configuration from a browser — autocompletion and documentation built in.