Niri in nixos-stable-25.05 black screen

I am trying to test out Niri on nixos-25.05 in a kubevirt vm.
I currently get the black screen which seems in line with the comment at https://yalter.github.io/niri/Getting-Started.html#nixosnix. I get the black screen.
I am able to successfully launch Niri from the wayland Cinnamon on the same vm. (yes, I realize that is a wierd combo, but I have users running Niri on LMDE at the moment)
I checked the current niri package and it has a dependency on mesa-libgbm-25.01, but the current nixos-25.05 mesa is 25.07

I keep seeing, “pin the version”; but I have been away from NixOS for awhile. Could someone please remind me how to do this?

I assume I need something like:

{ pkgs, lib, ... }: let
  version = "25.0.1";
  override = old: {
    inherit version;
    src = old.src.override {
      rev = "mesa-${version}";
      hash = lib.fakeHash;
    };
  };
in {
  hardware.graphics.package = pkgs.mesa.overrideAttrs override;
  hardware.graphics.package32 = pkgs.pkgsi686Linux.mesa.overrideAttrs override;
}

Would it be better to somehow update the version in the Niri package rather than reverting the entire system to an older version?

So I think I was able to set my mesa version with what I posted above. I apologize for asking and then answering myself, but I was very unsure that would work.
I am still getting the black screen. I wonder if it is virtio related.

So… If I were to deploy the same config bare metal , would it work?
I was trying to avoid a risk like that.

Wayland in vms is annoying, also you might as well use 25.11 (currently nixos-unstable) as 25.05 is about to be deprecated.

I tried unstable. Unfortunately, the result was no different.
The thing I don’t understand is why it will run underneath the wayland Cinnamon.
That makes me feel like it is misconfiguration rather than lack of a driver.
It does complain that none of the EGL devices are implemented, but it does enumerate an EGL device.

in case existing setups might help bisect things:

I found https://wiki.nixos.org/wiki/Bisecting, but I am still not sure how to go about bisecting. Is there a better tutorial somewhere?
This is strictly a test setup, so I don’t mind changing it to figure this out. There is no data on it and the configurfation.nix is very minimal. Running Niri on it was the first thing I tried, so apart from cinnamon, Niri, qemuGuest.enable, and setting xserver.videoDrivers to “virtio”; this is a config straight from the installer. I have not set it up for flakes yet, though that is something I plan to do after I successfully build out the basic parts of the config.

# Edit this configuration file to define what should be installed on
# your system.  Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).

{ config, pkgs, lib, ... }:
{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
    ];

  # Bootloader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  networking.hostName = "nixos"; # Define your hostname.
  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.

  # Configure network proxy if necessary
  # networking.proxy.default = "http://user:password@proxy:port/";
  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";

  # Enable networking
  networking.networkmanager.enable = true;

  # Enable network manager applet
  programs.nm-applet.enable = true;

  # Set your time zone.
  time.timeZone = "America/New_York";

  # Select internationalisation properties.
  i18n.defaultLocale = "en_US.UTF-8";

  i18n.extraLocaleSettings = {
    LC_ADDRESS = "en_US.UTF-8";
    LC_IDENTIFICATION = "en_US.UTF-8";
    LC_MEASUREMENT = "en_US.UTF-8";
    LC_MONETARY = "en_US.UTF-8";
    LC_NAME = "en_US.UTF-8";
    LC_NUMERIC = "en_US.UTF-8";
    LC_PAPER = "en_US.UTF-8";
    LC_TELEPHONE = "en_US.UTF-8";
    LC_TIME = "en_US.UTF-8";
  };

  # Enable the X11 windowing system.
  services.xserver.enable = true;
  services.xserver.videoDrivers = [ "virtio" ];

  # Enable the LXQT Desktop Environment.
  services.xserver.displayManager.lightdm.enable = true;
  services.xserver.desktopManager.cinnamon.enable = true;

  # Configure keymap in X11
  services.xserver.xkb = {
    layout = "us";
    variant = "";
  };

  # Enable CUPS to print documents.
  services.printing.enable = true;

  # Enable sound with pipewire.
  services.pulseaudio.enable = false;
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    # If you want to use JACK applications, uncomment this
    #jack.enable = true;

    # use the example session manager (no others are packaged yet so this is enabled by default,
    # no need to redefine it in your config for now)
    #media-session.enable = true;
  };

  # Enable touchpad support (enabled default in most desktopManager).
  # services.xserver.libinput.enable = true;

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.ladmin = {
    isNormalUser = true;
    description = "Local Admin";
    extraGroups = [ "networkmanager" "wheel" ];
    packages = with pkgs; [
    #  thunderbird
    ];
  };

  # Install firefox.
  programs.firefox.enable = true;

  # Allow unfree packages
  nixpkgs.config.allowUnfree = true;

  # List packages installed in system profile. To search, run:
  # $ nix search wget
  environment.systemPackages = with pkgs; [
    vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
    wget
    vscodium-fhs

    # Niri related packages
    xwayland-satellite
    alacritty
    fuzzel
    swaylock
    mako
    swayidle
  ];

  # Some programs need SUID wrappers, can be configured further or are
  # started in user sessions.
  # programs.mtr.enable = true;
  # programs.gnupg.agent = {
  #   enable = true;
  #   enableSSHSupport = true;
  # };

  # List services that you want to enable:
  services.qemuGuest.enable = true;
  services.spice-vdagentd.enable = true;  # For clipboard sharing

  # Enable the OpenSSH daemon.
  services.openssh.enable = true;

  # Open ports in the firewall.
  # networking.firewall.allowedTCPPorts = [ ... ];
  # networking.firewall.allowedUDPPorts = [ ... ];
  # Or disable the firewall altogether.
  # networking.firewall.enable = false;

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It‘s perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "25.05"; # Did you read the comment?

  programs.niri.enable = true;
  
  security.polkit.enable = true; # polkit
  services.gnome.gnome-keyring.enable = true; # secret service
  security.pam.services.swaylock = {};

  programs.waybar.enable = true; # top bar
}

hm, i’m not running an nvidia gpu myself, so my config may not help, in retrospect.

hm, i’m not sure i can find niri documenting a process for this - so maybe the approach in your snippet seems more sensible - tho building it may take a bit.

maybe other niri users could chime in - tho i think their matrix channel had some nixos users as well.

i had some older notes on bisecting from prior to the wiki article, but yeah, your challenge maybe seems more about finding a good configuration for now.

I finally just installed bare metal to laptop and this problem doesn’t exist there. That is annoying, but ultimately isn’t a problem.

A thing that has been driving me crazy with trying to setup the niri config file, the waybar config file, the fuzzel config file, and the swalock config file. The problem is that the nix package version of these applications seem to completly disreguard global configuration.

My users are all ldap through sssd. I have never been able to figure out a way to do a home-manager config that will work if someone logs into a system they have never logged into before, so I use global config for these files instead.

In the case of /etc/niri, /etc/xdg/waybar and /etc/xdg/fuzzel, I have been able to get away with using environment.etc, but in the case of swaylock that doesn’t seem to work.

My guess is that because swaylock packages in /etc/pam.d, it uses its /nix/store location for SYSCONFDIR. Is there any way I can add the swaylock config to my configuration.nix to be included in the /nix/store//etc/swaylock for the package? I read something about a patch section, but I am afraid I am still a bit rusty on all of this.

the wiki has a bit on patching packages - does that help?
to create the patch, one would generally check out the source repo (at the revision used by nixpkgs), edit/add code as desired (e.g. to set a value for SYSCONFDIR), then use like git diff --cached > my.patch to get the patch file of (staged) changes.

1 Like

I have been doing some research down this path. It seems that SYSCONFDIR is actually a configure option and the swaylock package doesn’t set SYSCONFDIR at all. I am guessing this means that it is looking at /usr/local/etc rather than /etc.
I suppose that leaves me with 2 options:

  1. The slightly more hacky adding of my config file to /usr/local/etc. I am not sure there is a good way to do this in a configuration.nix.
  2. Is an override appropriate for changing a build value? (This is slowly starting to come back to me).

So (admittedly using AI), I came up with this:

(swaylock.overrideAttrs (oldAttrs: { mesonFlags = oldAttrs.mesonFlags ++ [ "-Dsysconfdir=/etc" ]; }))

This does what I was scared of. The package can’t deploy because it then tries to install its pam file into /etc/pam.d
Strangely, the default config does not put its pam.d into /usr/local/etc/pam.d
I am a bit confused as to what to do at this point.

I was thinking maybe I could link the etc/swaylock in the derivation to the real /etc/swaylock or even make my config files into derivations so I could repeat them in all packages that were needed. So I tried various incarnations of this hack:

    (swaylock.overrideAttrs (oldAttrs: {
      meta = oldAttrs.meta // {
        installPhase = ''
            ln -s /etc/swaylock $out/etc/swaylock
          '';
      };
    }))

but it seems to have no effect.

After trying this even worse hack (which I would really like to know how to fix for future knowledge):

    (swaylock.overrideAttrs (oldAttrs: {
      postInstall = oldAttrs.postInstall or [ ]
      patches = oldAttrs.patches or [ ]
        ++ [
          ''
            diff --git a/main.c b/main.c
            index 07e6667..1124897 100644
            --- a/main.c
            +++ b/main.c
            @@ -960,7 +960,7 @@ static char *get_config_path(void) {
                    static const char *config_paths[] = {
                            "$HOME/.swaylock/config",
                            "$XDG_CONFIG_HOME/swaylock/config",
            -               SYSCONFDIR "/swaylock/config",
            +               "/etc/swaylock/config",
                    };

                    char *config_home = getenv("XDG_CONFIG_HOME");
          ''
        ];
    }))
  ];

I finally made this work:

    (swaylock.overrideAttrs (oldAttrs: {
      postInstall = oldAttrs.postInstall or [ ]
        ++ [ ''
          ln -s /etc/swaylock $out/etc/swaylock
        '' ];
    }))
  ];

1 Like