Skip to content

NixOS

For NixOS users, you can add Noctalia to your system configuration using flakes and the provided NixOS module.

To install Noctalia, you need to add a flake input and install the package.

Add the required inputs to your flake configuration:

flake.nix
{
description = "NixOS configuration with Noctalia";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
quickshell = {
url = "github:outfoxxed/quickshell";
inputs.nixpkgs.follows = "nixpkgs";
};
noctalia = {
url = "github:noctalia-dev/noctalia-shell";
inputs.nixpkgs.follows = "nixpkgs";
inputs.quickshell.follows = "quickshell"; # Use same quickshell version
};
};
outputs = inputs@{ self, nixpkgs, ... }: {
nixosConfigurations.awesomebox = nixpkgs.lib.nixosSystem {
modules = [
# ... other modules
./noctalia.nix
];
};
};
}

With the flake installed, we can access Noctalia packages and modules in our configuration.

In noctalia.nix or some other module of your choice, install the default Noctalia package from the new noctalia input:

noctalia.nix
{ pkgs, inputs, ... }:
{
# install pacakge
environment.systemPackages = with pkgs; [
inputs.noctalia.packages.${system}.default
# ... maybe other stuff
];
}

Rebuild with sudo nixos-rebuild switch --flake ..


If you have home manager installed, you can configure your Noctalia shell in Nix.

To configure Noctalia settings, you can use the Noctalia home manager module:

noctalia.nix
{ pkgs, inputs, ... }:
{
home-manager.users.drfoobar = {
# import the home manager module
imports = [
inputs.noctalia.homeModules.default
];
# configure options
programs.noctalia-shell = {
enable = true;
settings = {
# configure noctalia here; defaults will
# be deep merged with these attributes.
bar = {
density = "compact";
position = "right";
showCapsule = false;
widgets = {
left = [
{
id = "SidePanelToggle";
useDistroLogo = true;
}
{
id = "WiFi";
}
{
id = "Bluetooth";
}
];
center = [
{
hideUnoccupied = false;
id = "Workspace";
labelMode = "none";
}
];
right = [
{
alwaysShowPercentage = false;
id = "Battery";
warningThreshold = 30;
}
{
formatHorizontal = "HH:mm";
formatVertical = "HH mm";
id = "Clock";
useMonospacedFont = true;
usePrimaryColor = true;
}
];
};
};
colorSchemes.predefinedScheme = "Monochrome";
general = {
avatarImage = "/home/drfoobar/.face";
radiusRatio = 0.2;
};
location = {
monthBeforeDay = true;
name = "Marseille, France";
};
};
# this may also be a string or a path to a JSON file,
# but in this case must include *all* settings.
};
};
}

For more information on the available settings, see the configuration reference.

To configure Noctalia settings, you can use the Noctalia home manager module:

noctalia.nix
{ pkgs, inputs, ... }:
{
home-manager.users.drfoobar = {
# import the home manager module
imports = [
inputs.noctalia.homeModules.default
];
# configure options
programs.noctalia-shell = {
enable = true;
colors = {
# you must set ALL of these
mError = "#dddddd";
mOnError = "#111111";
mOnPrimary = "#111111";
mOnSecondary = "#111111";
mOnSurface = "#828282";
mOnSurfaceVariant = "#5d5d5d";
mOnTertiary = "#111111";
mOutline = "#3c3c3c";
mPrimary = "#aaaaaa";
mSecondary = "#a7a7a7";
mShadow = "#000000";
mSurface = "#111111";
mSurfaceVariant = "#191919";
mTertiary = "#cccccc";
};
# this may also be a string or a path to a JSON file.
};
};
}

Keybindings are configured in your compositor. You make IPC calls to Noctalia as explained in the keybinds section. The only difference is that you can call noctalia-shell instead of qs -c noctalia-shell.

To safely configure noctalia keybindings in the Niri flake, be sure to pass a list:

niri.nix
{ pkgs, inputs, ... }:
{
# ...
home-manager.users.drfoobar =
{ config, ... }:
{
# ...
programs.niri = {
settings = {
# ...
binds = with config.lib.niri.actions; {
"Mod+Space".action.spawn = [
"noctalia-shell" "ipc" "call" "launcher" "toggle" # ✅
];
"Mod+P".action.spawn =
"noctalia-shell ipc call powerPanel toggle"; # ❌
};
};
};
}

It may convenient to define a utility function to allow you to use simple strings:

niri.nix
{ pkgs, inputs, ... }:
let
noctalia = cmd: [
"noctalia-shell" "ipc" "call"
] ++ (pkgs.lib.splitString " " cmd);
in
{
# ...
home-manager.users.drfoobar =
{ config, lib, ... }:
{
# ...
programs = {
niri = {
# ...
settings = {
binds = with config.lib.niri.actions; {
# ...
"Mod+L".action.spawn = noctalia "lockScreen toggle";
"XF86AudioLowerVolume".action.spawn = noctalia "volume decrease";
"XF86AudioRaiseVolume".action.spawn = noctalia "volume increase";
"XF86AudioMute".action.spawn = noctalia "volume muteOutput";
# etc
};
};
};
};
}

The Noctalia shell can now be run in two ways: manually or using a systemd service

To run Noctalia using the flake, run:

Terminal window
noctalia-shell

With the Niri flake, you can then spawn Noctalia on startup like this:

niri.nix
{ pkgs, inputs, ... }:
{
# ...
home-manager.users.drfoobar = {
# ...
programs.niri = {
package = niri;
settings = {
# ...
spawn-at-startup = [
{
command = [
"noctalia-shell"
];
}
];
};
};
};
}

To setup a systemd service, import the Noctalia NixOS modules and enable the service:

noctalia.nix
{ pkgs, inputs, ... }:
{
# import the nixos module
imports = [
inputs.noctalia.nixosModules.default
];
# enable the systemd service
services.noctalia-shell.enable = true;
}

The service will start after graphical-session.target by default, which will work correctly out of the box for Niri and Hyprland with UWSM.

If you have some custom target, you can configure it with the target option:

noctalia.nix
{ pkgs, inputs, ... }:
{
# ...
services.noctalia-shell = {
enable = true;
target = "my-hyprland-session.target";
};
}

This target depends on how you configure your hyperland session systemd service.


There is no comprehensive documentation of all configuration options, but the default values are autogenerated and placed in a settings-default.json in the Noctalia shell repository. We retrieve a live Nixified version of these here for your convenience:

noctalia.nix
{ pkgs, inputs, ... }:
{
home-manager.users.drfoobar = {
imports = [
inputs.noctalia.homeModules.default
];
programs.noctalia-shell = {
enable = true;
settings = {
settingsVersion = 12;
bar = {
position = "top";
backgroundOpacity = 1;
monitors = [ ];
density = "default";
showCapsule = true;
floating = false;
marginVertical = 0.25;
marginHorizontal = 0.25;
widgets = {
left = [
{
id = "SystemMonitor";
}
{
id = "ActiveWindow";
}
{
id = "MediaMini";
}
];
center = [
{
id = "Workspace";
}
];
right = [
{
id = "ScreenRecorder";
}
{
id = "Tray";
}
{
id = "NotificationHistory";
}
{
id = "WiFi";
}
{
id = "Bluetooth";
}
{
id = "Battery";
}
{
id = "Volume";
}
{
id = "Brightness";
}
{
id = "Clock";
}
{
id = "ControlCenter";
}
];
};
};
general = {
avatarImage = "";
dimDesktop = true;
showScreenCorners = false;
forceBlackScreenCorners = false;
radiusRatio = 1;
screenRadiusRatio = 1;
animationSpeed = 1;
animationDisabled = false;
};
location = {
name = "Tokyo";
useFahrenheit = false;
use12hourFormat = false;
showWeekNumberInCalendar = false;
};
screenRecorder = {
directory = "";
frameRate = 60;
audioCodec = "opus";
videoCodec = "h264";
quality = "very_high";
colorRange = "limited";
showCursor = true;
audioSource = "default_output";
videoSource = "portal";
};
wallpaper = {
enabled = true;
directory = "";
enableMultiMonitorDirectories = false;
setWallpaperOnAllMonitors = true;
fillMode = "crop";
fillColor = "#000000";
randomEnabled = false;
randomIntervalSec = 300;
transitionDuration = 1500;
transitionType = "random";
transitionEdgeSmoothness = 0.05;
monitors = [ ];
};
appLauncher = {
enableClipboardHistory = false;
position = "center";
backgroundOpacity = 1;
pinnedExecs = [ ];
useApp2Unit = false;
sortByMostUsed = true;
};
dock = {
autoHide = false;
exclusive = false;
backgroundOpacity = 1;
floatingRatio = 1;
monitors = [ ];
pinnedApps = [ ];
};
network = {
wifiEnabled = true;
};
notifications = {
doNotDisturb = false;
monitors = [ ];
location = "top_right";
alwaysOnTop = false;
lastSeenTs = 0;
respectExpireTimeout = false;
lowUrgencyDuration = 3;
normalUrgencyDuration = 8;
criticalUrgencyDuration = 15;
};
osd = {
enabled = true;
location = "top_right";
monitors = [ ];
autoHideMs = 2000;
};
audio = {
volumeStep = 5;
volumeOverdrive = false;
cavaFrameRate = 60;
visualizerType = "linear";
mprisBlacklist = [ ];
preferredPlayer = "";
};
ui = {
fontDefault = "Roboto";
fontFixed = "DejaVu Sans Mono";
fontBillboard = "Inter";
monitorsScaling = [ ];
idleInhibitorEnabled = false;
};
brightness = {
brightnessStep = 5;
};
colorSchemes = {
useWallpaperColors = false;
predefinedScheme = "Noctalia (default)";
darkMode = true;
matugenSchemeType = "scheme-fruit-salad";
};
matugen = {
gtk4 = false;
gtk3 = false;
qt6 = false;
qt5 = false;
kitty = false;
ghostty = false;
foot = false;
fuzzel = false;
vesktop = false;
pywalfox = false;
enableUserTemplates = false;
};
nightLight = {
enabled = false;
forced = false;
autoSchedule = true;
nightTemp = "4000";
dayTemp = "6500";
manualSunrise = "06:30";
manualSunset = "18:30";
};
hooks = {
enabled = false;
wallpaperChange = "";
darkModeChange = "";
};
};
};
};
}