NixOS without a Display Manager

Hello! I’ve started to play with NixOS in my free time and it’s been a great experience :smile:. I’m quite keen on having a minimal setup without any extraneous packages (where it can be helped).

Something that I’ve been trying to get working without success is skipping the display manager entirely and just using startx to start my X server straight from the default virtual terminal after logging in as non-root.

I found this page on the Nix wiki, a couple of StackOverflow questions, and the Arch wiki quite helpful, and was hoping that once I had this working manually, I would write a derivation to handle the Xorg configs.

Unfortunately, I haven’t managed to get it working manually, I seem to be able to startx into a black screen, but I am unable to use bspwm (my window manager of choice). The X server log also comes up empty. I probably just have to spend a bit more time double checking my configs…

After giving this issue a read however, I started to wonder if the approach I was using was idiomatic Nix-ing. So I was wondering, are there any approaches that I can take to get this working that would fit the “Nix” way of working? Surely there must be a simpler way to do this? Thanks in advance! :bowing_man:

Unfortunately, I haven’t managed to get it working manually, I seem to be able to startx into a black screen, but I am unable to use bspwm (my window manager of choice). The X server log also comes up empty. I probably just have to spend a bit more time double checking my configs…

Do you have a mouse pointer?

After giving this issue a read however, I started to wonder if the approach I was using was idiomatic Nix-ing. So I was wondering, are there any approaches that I can take to get this working that would fit the “Nix” way of working? Surely there must be a simpler way to do this? Thanks in advance! :bowing_man:

Well, it is not a problem from the point of view of Nix, but the question is what are NixOS defaults and implementation details and how compatible they are with startx workflow.

No I don’t, is that how I tell everything is working?

I’m not sure I understand enough of the whole ecosystem, or what you’re saying, to give a decent reply, but it looks like the display manager is typically started using systemd. Would it make sense to just get rid of this service and move the responsibility of starting X (and the window manager) to startx? :smile:

Do you have a mouse pointer?
No I don’t, is that how I tell everything is working?

Not exactly, but blank black screen without mouse pointer has a large chance to be just a blank VT… Is there a blinking text-mode cursor in the top-left corner?

Would it make sense to just get rid of this service and move the responsibility of starting X (and the window manager) to startx?

X needs to grab control of console. If you let systemd control your machine, you need to do the console-grabbing via systemd a service. There probably is a way to do it via sudo that allows you to start a plain X and launch the rest directly from your user session, but it needs to go through systemctl at some point.

1 Like

X needs to grab control of console. If you let systemd control your machine, you need to do the console-grabbing via systemd a service. There probably is a way to do it via sudo that allows you to start a plain X and launch the rest directly from your user session, but it needs to go through systemctl at some point.

You can specify a target terminal on the X command line as vt<number>. Using this, you can start X in-place by specifying the current terminal you are using. This works because you already have grabbed the current console.

With startx this would look like:

startx /path/to/bspwm -- vt$(fgconsole)
1 Like

Interesting, in terms of specifying bspwm as an arg to startx, if I already have it executing in my .xinitrc, I’m assuming I don’t need to do that? :thinking:

To verify whether this is your actual problem, you could try to capture the logs manually, like this:

$ startx -- vt$(fgconsole) >&1 > ~/startx.log

I think, the major difference between my setup and the wiki page is that I configured X globally in my configuration.nix, but with autorun = false and exportConfiguration = true.

The second option symlinks the X config to /etc, so you needn’t specify all the config and driver lib directories manually (like in the wiki) because they are then found automatically.

Yes I was considering doing that after giving the X server file a better read (didn’t realize there was an exportConfiguration option).

Did you mean with environment.pathsToLink = [ "/etc" ]; in my configuration.nix?

I wasn’t too sure what the implications of symlinking are, considering it isn’t done by default. I couldn’t find great documentation suggesting why this is only enabled through an option, perhaps to save inode space?

I did exactly this, and fixed a few bugs with my configuration.nix and all seems to work. The following works according to my needs, but I’m open to suggestions/improvements (for an initx workflow).


In configuration.nix, the necessary parts are…

# ...

  environment.systemPackages = with pkgs; [
    bspwm
    sxhkd
    st # I use st for as my terminal emulator.
    # etc.

# This is not necessary with services.xserver.exportConfiguration = true, see below.
# environment.pathsToLink = [ "/etc" ];

  services.xserver = {
    enable = true;
    autorun = false; # Important!

    exportConfiguration = true; # Important!

    layout = "gb";
    # xkbOptions = "eurosign:e"; TODO.

    # videoDrivers = [ "intel" ]; TODO.
    resolutions = [
      { x = 1280; y = 720; }
      { x = 1920; y = 1080; }
      { x = 2560; y = 1440; }
    ];

    # Enable touchpad support.
    libinput.enable = true;

    # Disable desktop manager.
    desktopManager.default = "none";

    # Note that the default window manager is set to "none", and is not necessary.
    # Also avoid enabling any display manager. I think one of them is enabled by default, but oh well...
  };

# ...

Then I just run startx "$(command -v bspwm)" -- "vt""$XDG_VTNR" as @jorsn suggested. Could also move these commands into an .xserverrc and a .xinitrc to save typing it out every time.

No, i mean onle exportConfiguration = true, which is handeled by this code in the xserver.nix module:

environment.etc =
  (optionals cfg.exportConfiguration
    [ { source = "${configFile}";
        target = "X11/xorg.conf";
      }
      # -xkbdir command line option does not seems to be passed to xkbcomp.
      { source = "${cfg.xkbDir}";
        target = "X11/xkb";
      }
    ])
    #...

I think, with environment.pathsToLink you could pollute your profile because to my understanding this refers to the specified dirs in every package. I’m not sure what the main problem here is, but unnecessary state (s.b.) could be another one.

With exportConfiguration the only problem should be that you have one global X server config — state again that is not even atomic because /etc is not in a profile. So, e.g. if you want to have different configs in parallel you get problems. Then, imho, it would be better to write a nix config module to e.g. generate wrapper scripts for the different x envs, by invoking startx with the right parameters.

1 Like

Usually the correct resolutions should be detected automatically for each monitor (see xrandr).

Have you tried omitting the vt option for startx with this config? I tried and it works. I looked into the source and startx uses the current vt if none is specified and the default x server is used.

Yes! This was what I was considering at some point. :smile: In my case I don’t think that will crop up, but it is something I could consider contributing back to the Nix community…

Thanks - I wasn’t aware of this. For now I was specifying resolutions because I wanted it to be smaller intentionally (still playing around with NixOS in a VM).

At first I thought you were referring to the default .xserverrc, then I checked the path which startx points to and it looks like it doesn’t actually exist, which is interesting… It looks like, as you mentioned though startx already tries to start on the current tty. There are some additional options I would want to specify to Xorg so I might try defining my own .xserverrc anyway.