How configuration works
Noctalia can be configured in two ways: files you write yourself, and settings the app saves for you.
For most people, the important rule is simple: put your own configuration in ~/.config/noctalia/. If something changed in the GUI and now seems to ignore your file, check ~/.local/state/noctalia/settings.toml.
The two layers
Section titled “The two layers”Your hand-written config lives in:
$NOCTALIA_CONFIG_HOME/noctalia/$XDG_CONFIG_HOME/noctalia/- or
~/.config/noctalia/
Noctalia reads every *.toml file in that folder, sorts them alphabetically, and merges them into one config. A single config.toml is the easiest setup:
[theme]mode = "dark"
[bar.main]position = "top"You can also split things up if you like tidy dotfiles:
~/.config/noctalia/├── bar.toml├── theme.toml└── widgets.tomlGUI-managed overrides live in:
$NOCTALIA_STATE_HOME/noctalia/settings.toml$XDG_STATE_HOME/noctalia/settings.toml- or
~/.local/state/noctalia/settings.toml
Noctalia writes this file when you change settings through the UI, setup flows, IPC-backed controls, and other runtime actions that need to persist. If the file is a symbolic link, Noctalia writes through to the link target and keeps the link in place.
NOCTALIA_CONFIG_HOME and NOCTALIA_STATE_HOME have the same “home root” shape as the XDG variables. For example, NOCTALIA_CONFIG_HOME=/tmp/profile reads /tmp/profile/noctalia/. Use these Noctalia-specific variables when you want a separate shell profile without changing the environment inherited by apps launched from Noctalia.
Including extra files
Section titled “Including extra files”Automatic loading only sees *.toml files sitting directly in the config folder. To pull in files from subdirectories, load a specific file, or control load order without renaming files, add an [include] table:
[include]files = [ "widgets/", # a directory: every *.toml inside it (sorted, non-recursive) "bars/top.toml", # a single file "~/.config/shared/base.toml", # ~ , $VAR and ${VAR} are expanded]
# the rest of this file's settings go here as usual[theme]mode = "dark"Each entry is either a directory (which loads every *.toml directly inside it, sorted alphabetically) or a single file. Relative paths resolve against the directory of the file doing the including, so a file in profiles/work.toml can reach a shared bundle with "../widgets/". Paths starting with ~, or containing $VAR / ${VAR}, are expanded.
The including file wins. Included files are merged first, as a reusable base, and then the file’s own settings are layered on top. So a value you set directly in config.toml overrides the same value pulled in from an include:
[include]files = ["bars/base.toml"] # base.toml sets bar.top.thickness = 30
[bar.top]thickness = 40 # this wins - the bar ends up 40Includes can nest (an included file can include more files), and each file is loaded at most once - if two files include the same file, it is merged once and a cycle is detected and skipped rather than looping forever.
Switching profiles
Section titled “Switching profiles”By default, every *.toml in the config folder still loads automatically alongside your includes. If you keep alternative setups in the root that should not all load at once - say profile_a.toml and profile_b.toml - set autoload = false in your entry file. Then only the file(s) that set it (plus whatever they include) load, and the other root files are ignored:
[include]autoload = falsefiles = ["profiles/work.toml"]autoload only has an effect in a file loaded directly from the config folder; it is ignored in included files. [include] is honored in your config files only - it has no effect in the app-managed settings.toml.
Which value wins?
Section titled “Which value wins?”Noctalia loads configuration in this order:
- Built-in defaults
- Your
*.tomlfiles in the resolved config directory - for each file, anything it pulls in via[include]first, then the file’s own settings on top - GUI-managed overrides in the resolved state directory’s
settings.toml
Because settings.toml loads last, it wins when it contains the same setting as your hand-written config layer.
When Settings writes a value that matches the parsed value from the lower layers, Noctalia removes that redundant key
instead of keeping it as a GUI override.
Where should I edit?
Section titled “Where should I edit?”Use ~/.config/noctalia/ for curated config: the things you want to keep in dotfiles, copy to another machine, or manage declaratively.
Treat ~/.local/state/noctalia/settings.toml as an app-managed override file. It is useful to inspect when debugging GUI changes, and safe to delete when you want to clear those overrides. Keeping it outside ~/.config also lets the GUI save changes when your config directory is read-only, for example on NixOS. Support reports include config sources and settings.toml, but omit state.toml contents because they can contain private runtime data.
Both layers are watched for changes and hot-reloaded. If neither layer exists, Noctalia uses its built-in defaults.
Automatic configuration migrations
Section titled “Automatic configuration migrations”When a release changes the shape of a setting, Noctalia migrates old values before parsing them so the running shell
keeps the intended behavior. The app-owned settings.toml is upgraded once and stamped with an internal
config_version; Noctalia manages that marker and excludes it from exported user config.
Files under ~/.config/noctalia/ are never rewritten. Values that need migration are normalized in memory on every
load, and Settings keeps a warning visible until you update the source TOML. Noctalia also sends a notification when
it first finds an affected path, when a new affected path appears, and every three days while affected values remain.
Run the validator for every affected path and its canonical replacement:
noctalia config validateNoctalia can keep using normalized values while you update a file, but migration warnings are intentionally not permanent compatibility promises. Treat them as required config maintenance.
File locations
Section titled “File locations”Where Noctalia keeps everything, by purpose:
| What | Where | Purpose |
|---|---|---|
| Your config | ~/.config/noctalia/config.toml | hand-written / declarative - the base layer |
| GUI overrides | ~/.local/state/noctalia/settings.toml | written by Settings; wins over your config |
| Internal UI state | ~/.local/state/noctalia/state.toml | app-managed (calendar account tokens, last-used values) |
| Custom palettes | ~/.config/noctalia/palettes/ | your own color palette files |
| Local plugins | ~/.local/share/noctalia/plugins/ | plugins you install or place by hand |
| Plugin source repos | ~/.local/state/noctalia/plugins/sources/ | git-source repo caches, re-fetchable |
| Exported plugin files | ~/.local/state/noctalia/plugins/materialized/ | runtime files exported from git sources |
| Community palettes / templates | ~/.local/state/noctalia/community-*/ | downloaded catalogs, re-fetchable |
It comes down to two buckets:
- Yours -
~/.config/noctalia/(settings you write) and~/.local/share/noctalia/(plugins you install). - Noctalia’s -
~/.local/state/noctalia/(everything the app writes or fetches: GUI overrides, UI state, plugin repo caches, exported plugin files, downloaded catalogs) - app-managed and regenerable, so it’s always safe to delete.
Every path honors the standard XDG_CONFIG_HOME / XDG_STATE_HOME / XDG_DATA_HOME variables (and the
NOCTALIA_CONFIG_HOME / NOCTALIA_STATE_HOME / NOCTALIA_DATA_HOME overrides).
Exporting config
Section titled “Exporting config”Export the merged user config from the command line:
noctalia config export > noctalia-config.tomlExport the full effective config, including built-in defaults:
noctalia config export full > noctalia-full-config.tomlExport stops with an error (and writes nothing) if a config file fails to parse or an [include] points at a file that does not exist, so a broken config never produces a misleading partial export. (The running shell is more forgiving: it loads what it can and surfaces the problem as a notification.)
Because export writes TOML to stdout, you can pipe it into TOML-aware query tools. For example, with
yq:
noctalia config export full | yq -p toml -r '.theme.mode'noctalia config export full | yq -p toml -r '.shell.offline_mode'noctalia config export full | yq -p toml '.bar.default'If you prefer normal jq filters, convert the TOML stream to JSON first:
noctalia config export full | yq -p toml -o json '.' | jq -r '.theme.mode'The Settings actions menu includes Export Config…. It opens a chooser with two export modes:
- Merged User Config is recommended for dotfiles. It merges your explicit config files and GUI-managed overrides into one TOML file, while leaving built-in defaults implicit.
- Full Effective Config exports the active config snapshot, including built-in defaults. Use it for inspection or sharing an exact state, but avoid treating it as your long-term curated config because it pins defaults that could otherwise evolve.
Validating your configuration
Section titled “Validating your configuration”Run the validator to catch problems before they silently take effect:
noctalia config validateWith no argument it checks the merged configuration exactly as the shell loads it: every *.toml in ~/.config/noctalia/ (or $NOCTALIA_CONFIG_HOME), then the settings.toml overrides in ~/.local/state/noctalia/. Pass a directory to validate just that directory’s *.toml files instead:
noctalia config validate ./my-config-dirPass a file to validate only that TOML file, without scanning default locations or merging settings.toml. This is useful for generated configs, including NixOS-managed config files:
noctalia config validate ./config.tomlIt reports two severities:
- Errors - problems that stop the configuration from loading correctly: TOML syntax errors (with file, line, and column), values that fail to parse such as an invalid color, an
[include]whosefilesentry points at a path that does not exist, or a malformed[include]table (autoloadthat is not a boolean,filesthat is not a list of strings). These exit with status1. - Warnings - things the shell loads fine but ignores, adjusts, or migrates in memory: unknown/obsolete sections and settings (misspelled or left over from an older version), values with a canonical replacement, values outside the allowed range (clamped), and invalid enum choices. Warnings are advisory and do not fail the check. Unknown widget and desktop-widget settings are checked against each widget type’s known options.
A config with only warnings still prints ✓ Config is valid and exits 0 (with the warnings listed), so unknown keys won’t break a pre-commit hook or CI step - they’re surfaced as cleanup hints. Only genuine errors exit 1.
Where next?
Section titled “Where next?”- Shell covers global behavior like fonts, scale, shadows, OSD, and keybinds.
- Hooks covers advanced event-driven automation.
- Bars and Widgets cover the top or side bar and everything inside it.
- Dock covers the optional app dock.
- Theme covers theme mode and palette source selection.
- Palette covers color roles, custom palette files, and terminal colors.
- App Theming covers generated themes for other apps.
- Template Reference covers template syntax, tokens, filters, and hooks.
- Wallpaper & backdrop covers wallpapers, per-monitor folders, automation, and overview backdrop.
- Service pages cover audio, brightness, calendar, idle, night light, notifications, and weather.
- IPC lists commands for keybinds, scripts, automation, and hooks.