Skip to content

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.

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:

~/.config/noctalia/config.toml
[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.toml

GUI-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.

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:

~/.config/noctalia/config.toml
[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 40

Includes 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.

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:

~/.config/noctalia/config.toml
[include]
autoload = false
files = ["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.

Noctalia loads configuration in this order:

  1. Built-in defaults
  2. Your *.toml files in the resolved config directory — for each file, anything it pulls in via [include] first, then the file’s own settings on top
  3. 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.

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.

Where Noctalia keeps everything, by purpose:

WhatWherePurpose
Your config~/.config/noctalia/config.tomlhand-written / declarative — the base layer
GUI overrides~/.local/state/noctalia/settings.tomlwritten by Settings; wins over your config
Internal UI state~/.local/state/noctalia/state.tomlapp-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).

Export the merged user config from the command line:

Terminal window
noctalia config export > noctalia-config.toml

Export the full effective config, including built-in defaults:

Terminal window
noctalia config export full > noctalia-full-config.toml

Export 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:

Terminal window
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:

Terminal window
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.

Run the validator to catch problems before they silently take effect:

Terminal window
noctalia config validate

With 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:

Terminal window
noctalia config validate ./my-config-dir

Pass 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:

Terminal window
noctalia config validate ./config.toml

It 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] whose files entry points at a path that does not exist, or a malformed [include] table (autoload that is not a boolean, files that is not a list of strings). These exit with status 1.
  • Warnings — things the shell loads fine but quietly ignores or adjusts: unknown/obsolete sections and settings (misspelled or left over from an older version), 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.

Scripted-widget settings come from each script’s own manifest, so unknown keys under a scripted widget are not flagged.