Declarative Configuration for Desktop Environments on NixOS

As soon as I tried out Nix, I wanted to use Nix for everything. With Nix, all your configuration is reproducible (as long as you stay in pure mode, that is), which is amazing if you need to share configurations across devices.

For the sake of this article, I'll assume you have a working knowledge of Nix and NixOS.

Getting Started

Building on top of Nix, home-manager is great for managing non-global packages and dotfiles. It also has a module called dconf that allows you to declaratively define gsettings, a settings system for GNOME and related desktop environments.

Using the dconf module, you can configure everything from your system dark theme, desktop background image, and global keyboard shortcuts declaratively using Nix.

First you need to enable dconf; in your /etc/nixos/configuration.nix or flake.nix add:

{ pkgs, ... }: { programs.dconf.enable = true; }

Then, in your home-manager configuration, you can start configuring your desktop environment using dconf.

{ pkgs, ... }: {
  dconf.settings = {
    # place your dconf settings here
    "some/settings/namespace" = { some-setting = "some-value"; };
  };
}

Finding the Correct Settings Namespaces and Keys

Now that you've enabled dconf in your NixOS configuration, we can use dconf from the command line to figure out what the namespaces and keys are for the settings you want to set declaratively in Nix. To do this, open a new terminal window and run dconf watch /. This will start a process which will output any settings value that changes.

Now, change your desired settings from the settings GUI, and observe the output from dconf watch /. For example, changing your wallpaper image from the settings GUI should output something like:

/org/gnome/desktop/background/picture-uri
  'file:///home/mat/.local/share/backgrounds/2023-06-15-14-59-58-wallpaper.jpg'

Declarative Settings

With this information, you can set your desktop wallpaper declaratively within your home-manager configuration. You can use pkgs.fetchurl to fetch the desired image file and set it as your desktop wallpaper:

{ pkgs, ... }:
let
  wallpaperImg = pkgs.fetchurl {
    url = "https://url/to/your/image";
    # replace this with the SHA256 hash of the image file
    hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  };
in {
  dconf.settings = {
    "org/gnome/desktop/background" = {
      picture-uri = "file://${wallpaperImg}";
    };
  };
}

With this added, applying your home-manager configuration will change your desktop wallpaper! You can use this method to find just about all the settings you'll need, although some of them might require some fiddling with.

For example, creating custom keyboard shortcuts is slightly more complex. First, you have to specify each custom shortcut group that should exist, then you can specify the shortcuts themselves. For example:

{ pkgs, ... }: {
  dconf.settings = {
    "org/gnome/settings-daemon/plugins/media-keys" = {
      # specify that custom shortcut "custom0" should exist and be included
      custom-keybindings = [
        "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/"
      ];
      terminal = [ "" ];
    };
    # now you can specify the "custom0" shortcut itself
    "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0" =
      {
        binding = "Print"; # printscrn key
        command = "flameshot gui";
        name = "flameshot";
      };
  };
}

Feel free to take a look at all the current settings I use at time of writing in my dotfiles repo.