How to start xorg via systemd?

Hello everyone, I’ve installed nixos a couple of days ago.
One of the difficulties I had was how to startx xorg via systemd socket activation.
I need this to transfer my old home stuff on arch to the new distro.

startx works fine overall, but because of it’s not started by systemd some stuff like ~/.config/environment.d is not loaded.

But in general, it’s not clear what’s missing, since xinit and the parameters may be the same.

Trying to start x11.service I get

xf86OpenConsole: Cannot open virtual console 1 (Permission denied)

I’ve tried change default tty rules via

services.udev.extraRules = ''
    SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
    SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666"
    SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620"
    SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620"
    SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620"
    SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620"
    SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty"
    KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="uucp"

    SUBSYSTEM=="input", GROUP="input"
    SUBSYSTEM=="input", KERNEL=="js[0-9]*", MODE="0664"

    SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664"

    KERNEL=="rfkill", MODE="0664"
'';

without success

My systemwide config:

 /mnt/loop1/etc/nixos ❯> cat configuration.nix filesystems.nix networking.nix nvidia.nix
# Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).
{ config, lib, pkgs, modulesPath, packageOverrides, ... }:
{
    imports = [
        (modulesPath + "/installer/scan/not-detected.nix")
        ./filesystems.nix
        ./networking.nix
        ./nvidia.nix
    ];
    nix.extraOptions = ''experimental-features = nix-command flakes'';
    boot.loader.systemd-boot.enable = true;
    boot.loader.efi.canTouchEfiVariables = true;
    boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "usb_storage" "usbhid" "sd_mod" ];
    boot.initrd.kernelModules = [ "dm-snapshot" ];
    boot.kernelModules = [ "kvm-amd" ];
    boot.extraModulePackages = [ ];
    nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
    hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
    networking.hostName = "telfir"; # Define your hostname.
    networking.wireless.enable = false;  # Enables wireless support via wpa_supplicant.
    networking.networkmanager.enable = true;
    time.timeZone = "Europe/Moscow";
    i18n.defaultLocale = "en_US.UTF-8";
    i18n.extraLocaleSettings = {
        LC_ADDRESS = "ru_RU.UTF-8";
        LC_IDENTIFICATION = "ru_RU.UTF-8";
        LC_MEASUREMENT = "ru_RU.UTF-8";
        LC_MONETARY = "ru_RU.UTF-8";
        LC_NAME = "ru_RU.UTF-8";
        LC_NUMERIC = "ru_RU.UTF-8";
        LC_PAPER = "ru_RU.UTF-8";
        LC_TELEPHONE = "ru_RU.UTF-8";
        LC_TIME = "ru_RU.UTF-8";
    };
    services.xserver.enable = true;
    services.xserver.displayManager.startx.enable = true;
    services.xserver.displayManager.autoLogin.enable = true;
    services.xserver.displayManager.autoLogin.user = "neg";
    systemd.packages = [ pkgs.packagekit ];
    services.xserver = {
        layout = "us,ru";
        xkbVariant = "";
        xkbOptions = "grp:alt_shift_toggle";
    };

    security.polkit.enable = true;
    security.loginDefs.settings.TTYPERM = "0666";
    security.sudo.extraRules= [ {
        users = [ "privileged_user" ];
            commands = [
            { command = "ALL" ;
                options= [ "NOPASSWD" ]; # "SETENV" # Adding the following could be a good idea
            }
            ];
        }
    ];
    security.pam.loginLimits = [
        { domain = "@users"; item = "rtprio"; type = "-"; value = 1; }
    ];
    security.pam.services.xserver.startSession = true;
    security.pam.services.xserver.forwardXAuth = true;
    # security.pam.enableGnomeKeyring = true;

    # This is using a rec (recursive) expression to set and access XDG_BIN_HOME within the expression
    # For more on rec expressions see https://nix.dev/tutorials/first-steps/nix-language#recursive-attribute-set-rec
    environment.sessionVariables = rec {
        XDG_CACHE_HOME  = "$HOME/.cache";
        XDG_CONFIG_HOME = "$HOME/.config";
        XDG_DATA_HOME   = "$HOME/.local/share";
        XDG_STATE_HOME  = "$HOME/.local/state";
        XDG_BIN_HOME    = "$HOME/.local/bin";
        PATH = [ "${XDG_BIN_HOME}" ];
    };

    fonts.packages = with pkgs; [
        noto-fonts
        noto-fonts-cjk
        noto-fonts-emoji
        liberation_ttf
        iosevka
    ];

    services.printing.enable = false;
    hardware.pulseaudio.enable = false;
    security.rtkit.enable = true; # rtkit is optional but recommended
        services.pipewire = {
            enable = true;
            alsa.enable = true;
            alsa.support32Bit = true;
            pulse.enable = true;
            jack.enable = true;
        };

    services.xserver.libinput.enable = true; # Enable touchpad support (enabled default in most desktopManager).
    # Define a user account. Don't forget to set a password with ‘passwd’.
    users.users.neg = {
        isNormalUser = true;
        description = "Neg";
        extraGroups = [
            "audio"
            "neg"
            "networkmanager"
            "systemd-journal"
            "video"
            "wheel"
            "tty"
            "input"
        ];
    };

    users.defaultUserShell = pkgs.zsh;
    users.groups.neg.gid = 1000;
    nixpkgs.config.allowUnfree = true;
    environment.systemPackages = with pkgs; [
        curl
        gcc
        gdb
        git
        neovim
        nix-index
        python3
        tig
        tmux
        wget
        zsh
    ];

    environment.shells = with pkgs; [ zsh ];
    programs.dconf.enable = true;
    programs.mtr.enable = true;
    programs.zsh = { enable = true; };
    services.openssh.enable = true;
    services.flatpak.enable = true;
    services.getty.autologinUser = "neg";

    xdg.portal.enable = true;
    xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
    xdg.portal.config.common.default = "gtk";

    # (man configuration.nix or on https://nixos.org/nixos/options.html).
    system.stateVersion = "23.11"; # Did you read the comment?
}
{ config, lib, pkgs, modulesPath, ... }:
{
  # UUID=C06B-349A /boot  vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0  2
  # /dev/main/sys   /                         f2fs      rw,relatime,lazytime,background_gc=off,no_heap,inline_xattr,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,alloc_mode=default,fsync_mode=posix                                                                                  0  1
  # /dev/main/home  /home                     f2fs      x-systemd.automount,rw,relatime,lazytime,background_gc=off,no_heap,inline_xattr,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,alloc_mode=default,fsync_mode=posix                                                       0  1
  # /dev/argon/zero                            /zero                     f2fs      rw,defaults,x-systemd.automount,relatime,lazytime     0  0
  # /dev/xenon/one                             /one                      f2fs      rw,defaults,x-systemd.automount,relatime,lazytime     0  0
  # /one/music                                 /home/neg/music           none      nofail,x-systemd.automount,bind                       0  0
  # /one/torrent                               /home/neg/torrent         none      nofail,x-systemd.automount,bind                       0  0
  # /one/vid                                   /home/neg/vid             none      nofail,x-systemd.automount,bind                       0  0
  # /one/games                                 /home/neg/games           none      nofail,x-systemd.automount,bind                       0  0
  # /one/mail                                  /home/neg/.local/mail     none      nofail,x-systemd.automount,bind                       0  0
  # /zero/opt                                  /opt                      none      nofail,x-systemd.automount,bind                       0  0
  # /zero/flatpak                              /home/neg/.var            none      nofail,x-systemd.automount,bind                       0  0
  # tmpfs                                      /dev/shm                  tmpfs     defaults,rw,nosuid,noexec,nodev,size=32g              0  0

  fileSystems."/" =
    { device = "/dev/mapper/xenon-nix";
      fsType = "f2fs";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/C06B-349A";
      fsType = "vfat";
    };

  fileSystems."/home" =
    { device = "/dev/mapper/main-home";
      fsType = "f2fs";
    };

  fileSystems."/one" =
    { device = "/dev/mapper/xenon-one";
      fsType = "f2fs";
    };

  fileSystems."/zero" =
    { device = "/dev/mapper/argon-zero";
      fsType = "f2fs";
    };

  fileSystems."/home/neg/music"={device="/one/music"; options=["bind"];};
  fileSystems."/home/neg/torrent"={device="/one/torrent"; options=["bind"];};
  fileSystems."/home/neg/vid"={device="/one/vid"; options=["bind"];};
  fileSystems."/home/neg/games"={device="/one/games"; options=["bind"];};
  fileSystems."/home/neg/.var"={device="/zero/flatpak"; options=["bind"];};
  swapDevices = [ ];
}
{ config, lib, pkgs, modulesPath, ... }:
{
    # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
    # (the default) this is the recommended approach. When using systemd-networkd it's
    # still possible to use this option, but it's recommended to use it in conjunction
    # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
    networking.useDHCP = lib.mkDefault true;
    # networking.interfaces.enp6s0.useDHCP = lib.mkDefault true;
    # networking.interfaces.enp8s0.useDHCP = lib.mkDefault true;
}
{ config, lib, pkgs, ... }:
{
    hardware.opengl = {
        enable = true;
        driSupport = true;
        driSupport32Bit = true;
    };
    boot.blacklistedKernelModules = [ "nouveau" ];
    boot.extraModprobeConfig = ''
        blacklist nouveau
        options nouveau modeset=0
        '';

    environment = {
        systemPackages = with pkgs;
        [ glxinfo ];
        variables = { __GL_GSYNC_ALLOWED = "1"; };
    };

    services.xserver = {screenSection = ''Option         "metamodes" "3440x1440_175 +0+0 {AllowGSYNCCompatible=On}"'';};
    services.xserver.videoDrivers = ["nvidia"];
    hardware.nvidia = {
        modesetting.enable = true; # Modesetting is required.
        # Nvidia power management. Experimental, and can cause sleep/suspend to fail. powerManagement.enable = false;
        # Fine-grained power management. Turns off GPU when not in use.
        # Experimental and only works on modern Nvidia GPUs (Turing or newer).
        powerManagement.finegrained = false;
        open = false; # Currently alpha-quality/buggy, so false is currently the recommended setting.
        nvidiaSettings = true;
        #package = config.boot.kernelPackages.nvidiaPackages.stable;
    };
    # services.udev.extraRules = ''
    #     SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
    #     SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666"
    #     SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620"
    #     SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620"
    #     SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620"
    #     SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620"
    #     SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty"
    #     KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="uucp"
    #
    #     SUBSYSTEM=="input", GROUP="input"
    #     SUBSYSTEM=="input", KERNEL=="js[0-9]*", MODE="0664"
    #
    #     SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664"
    #
    #     KERNEL=="rfkill", MODE="0664"
    # '';
}

and here is the home stuff

 ~/.config/home-manager ❯> cat home.nix
{ config, pkgs, ... }:
{
  home.username = "neg";
  home.homeDirectory = "/home/neg";
  home.stateVersion = "23.11"; # Please read the comment before changing.
  home.packages = with pkgs; [
      acpi
      bat
      dash
      dunst
      fasd
      ffmpeg
      firefox
      flameshot
      gnome.gpaste
      hsetroot
      i3
      inputplug
      jq
      kitty
      libnotify
      lshw
      maim
      mpc-cli
      mpd
      mpdas
      mpdris2
      mpv
      ncmpcpp
      ncpamixer
      neovim-remote
      netcat
      nsxiv
      pass
      picom
      polybar
      python3
      python311Packages.pip
      rofi
      # stig
      stow
      telegram-desktop
      transmission
      udisks2
      unclutter-xfixes
      unzip
      wmctrl
      xclip
      xdotool
      xsel
      xterm
      ydotool
      yt-dlp
      zathura
  ];

  services.udiskie = { enable = true; };
  services.gpg-agent = {
      enable = true;
      enableScDaemon = true;
      enableSshSupport = true;
      defaultCacheTtl = 60480000;
      pinentryFlavor = "gnome3";
      verbose = true;
  };

  systemd.user.targets.tray = {
      Unit = {
          Description = "Home Manager System Tray";
          Requires = ["graphical-session-pre.target"];
      };
  };

  systemd.user.services.i3 = {
      Unit = {
        Description = "i3 improved dynamic tiling window manager for X";
        Documentation = "man:i3(5)";
        Wants = ["graphical-session-pre.target" "i3-session.target"];
        After = ["graphical-session-pre.target" "i3-session.target"];
        BindsTo = ["graphical-session.target"];
        PartOf = ["graphical-session.target"];
        #OnFailure = notify@%i.service
      };
      Service = {
          ExecStart = "${pkgs.i3}/bin/i3";
          ExecReload = ["${pkgs.i3}/i3-msg reload" "${pkgs.systemd}/bin/systemctl --user restart negwm.service"];
          ExecStopPost = "${pkgs.systemd}/bin/systemctl --user stop --no-block graphical-session.target";
          Restart = "on-failure";
          RestartSec = "5";
          TimeoutStopSec = "10";
      };
  };

  systemd.user.services.mpdris2 = {
      Unit = {
        Description = "mpDris2 - Music Player Daemon D-Bus bridge";
        Requires = "mpd.service";
        BindsTo = "mpd.service";
        ConditionPathExists = "${pkgs.mpdris2}/bin/mpDris2";
        # OnFailure=notify@%i.service
      };
      Service = {
          Restart = "on-failure";
          ExecStart = "${pkgs.mpdris2}/bin/mpDris2 --use-journal";
          BusName = "org.mpris.MediaPlayer2.mpd";
      };
      Install = { WantedBy = ["default.target"]; };
  };

  systemd.user.services.negwm-autostart = {
    Unit = {
        Description = "Startup stuff depended on negwm";
        Requires = "negwm.service";
        After = ["negwm.service" "executor.service"];
        #OnFailure = "notify@%i.service";
    };
    Service = {
        Type = "oneshot";
        ExecStart = "${pkgs.dash}/bin/dash -c \"echo 'circle next term' | ${pkgs.netcat}/bin/nc localhost 15555 -w 0\"";
        Restart = "on-failure";
        RestartSec = "1";
        StartLimitBurst = "20";
    };
  };

  systemd.user.services.negwm = {
      Unit = {
          Description = "negwm window manager mod for i3wm";
          PartOf = ["graphical-session.target"];
          StartLimitBurst = "5";
          StartLimitIntervalSec = "0";
          Requires = ["xsettingsd.service"];
          #   OnFailure=notify@%i.service
      };
      Service = {
          ExecStart = "%h/bin/negwm";
          Restart = "on-failure";
      };
  };

  systemd.user.services.x11 = {
      Unit = {
        Description = "Xorg";
        Documentation = "man:Xorg(1)";
        Requires = ["x11.socket"];
        After = "x11.socket";
        Before = "x11.target";
        #OnFailure = "notify@%i.service";
      };
      Service = {
        Environment = ["XDG_SESSION_TYPE=x11" "XSERVERRC=%E/xinit/xserverrc"];
        ExecStartPre = "${pkgs.dbus}/bin/dbus-update-activation-environment --all";
        ExecStart = "${pkgs.xorg.xinit}/bin/xinit ${pkgs.i3}/bin/i3 -- :0 -nolisten tcp vt1";
        SuccessExitStatus = "0 1";
      };
      Install = {
        Also = ["x11.socket" "x11.target"];
        WantedBy = ["default.target"];
      };
  };

  systemd.user.services.picom = {
      Unit = {
        Description = "Compositing manager";
        Documentation = "man:Xorg(1)";
        After = "dbus.socket";
        Before = "x11.target";
        PartOf = ["graphical-session.target"];
        StartLimitIntervalSec = "60";
        Requires = ["x11.service"];
        OnFailure = "notify@%i.service";
      };
      Service = {
          ExecStart = "${pkgs.picom}/bin/picom --dbus --backend glx";
          ExecReload = "${pkgs.util-linux}/bin/kill -SIGUSR1 $MAINPID";
          Restart = "on-failure";
          RestartSec = "1";
          StartLimitBurst = "3000";
      };
  };

  systemd.user.services.inputplug = {
      Unit = {
        Description = "XInput event monitor";
        PartOf = ["graphical-session.target"];
        OnFailure = ["notify@%i.service"];
      };
      Service = {
        ExecStart = "${pkgs.inputplug}/bin/inputplug -d -0 -c %h/bin/input-event";
        Restart = "on-failure";
      };
  };

  systemd.user.services.cover-notify = {
      Unit = {
        Description = "Music track notification with cover";
        After = ["network.target" "sound.target" "playerctld.service" "mpd.service" "mpDris2.service"];
        BindsTo = "mpDris2.service";
        OnFailure = ["notify@%i.service"];
      };
      Service = {
        ExecStart = "%h/bin/track-notification-daemon";
        Restart = "always";
        StartLimitIntervalSec = "0";
        RestartSec = "3";
      };
      Install = {
        WantedBy = ["default.target"];
      };
  };

  systemd.user.services.openrgb = {
      Unit = {
        Description = "OpenRGB Configuration utility for RGB lights supporting motherboards, RAM, & peripherals";
        After = ["dbus.socket"];
        PartOf = ["graphical-session.target"];
        OnFailure = ["notify@%i.service"];
      };
      Service = {
        ExecStart = "${pkgs.openrgb}/bin/openrgb --server -p neg.orp";
        RestartSec = "30";
        StartLimitBurst = "8";
      };
      Install = {
        WantedBy = ["default.target"];
      };
  };

  systemd.user.services.polybar = {
    Unit = {
        Description = "Polybar statusbar";
        PartOf = ["graphical-session.target"];
        StartLimitIntervalSec = "60";
        Requires = "xsettingsd.service";
        BindsTo = "xsettingsd.service";
        OnFailure = "notify@%i.service";
    };
    Service = {
        ExecStart = "${pkgs.polybar}/bin/polybar -q main";
        ExecStop = "${pkgs.polybar}/bin/polybar-msg cmd quit";
        Restart = "on-failure";
        RestartSec = "3";
        StartLimitBurst = "30";
    };
  };

  systemd.user.services.mpdas = {
    Unit = {
        Description = "mpdas last.fm scrobbler";
        After = ["network.target" "sound.target" "mpd.service"];
        Requires = "mpd.service";
        OnFailure = "notify@%i.service";
    };
    Service = {
        ExecStart = "${pkgs.mpdas}/bin/mpdas -c %E/mpdas/neg.rc";
        Restart = "on-failure";
        RestartSec = "10";
    };

    Install = {
        WantedBy = ["default.target"];
    };
  };

  systemd.user.services.mpd = {
    Unit = {
        Description = "Music Player Daemon";
        Documentation = "man:mpd(1) man:mpd.conf(5)";
        After = ["network.target" "sound.target"];
        ConditionPathExists = "${pkgs.mpd}/bin/mpd";
        OnFailure = "notify@%i.service";
    };
    Service = {
        Type = "notify";
        ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon";
        # Enable this setting to ask systemd to watch over MPD, see
        # systemd.service(5).  This is disabled by default because it causes
        # periodic wakeups which are unnecessary if MPD is not playing.
        #WatchdogSec=120
        # allow MPD to use real-time priority 40
        LimitRTPRIO = "40";
        LimitRTTIME = "infinity";
        # for io_uring
        LimitMEMLOCK = "64M";
        # disallow writing to /usr, /bin, /sbin, ...
        ProtectSystem = "yes";
        # more paranoid security settings
        NoNewPrivileges = "yes";
        ProtectKernelTunables = "yes";
        ProtectControlGroups = "yes";
        # AF_NETLINK is required by libsmbclient, or it will exit() .. *sigh*
        RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK"];
        RestrictNamespaces = "yes";

        # Note that "ProtectKernelModules=yes" is missing in the user unit
        # because systemd 232 is unable to reduce its own capabilities
        # ("Failed at step CAPABILITIES spawning /usr/bin/mpd: Operation not
        # permitted")

        Restart = "on-failure";
        RestartSec = "5";
    };
    Install = {
        WantedBy = ["default.target"];
    };
  };

  systemd.user.sockets.x11 = {
    Unit = {
        Description = "X11 Server Socket";
        Before = ["x11.target"];
    };
    Socket = {
        ListenStream = "/tmp/.X11-unix/X0";
    };
    Install = {
        WantedBy = ["sockets.target"];
    };
  };

  home.file = {
    ".config/xinit/xserverrc".text = ''
    exec ${pkgs.xorg.xorgserver}/bin/Xorg -ardelay 250 -arinterval 20 -nolisten tcp "$@" vt$XDG_VTNR
    '';
  };

  home.sessionVariables = {
    # EDITOR = "emacs";
  };
  programs.home-manager.enable = true; # Let Home Manager install and manage itself.
}

The problem is that it seems that systemd does not have enough rights to manage the tty or something like that. Perhaps x-server itself requires a suid bit, not sure yet.

Maybe I need some specific pam stuff to make it work, but not sure what exactly.

Using chown 0777 /dev/tty* also does not make things better.

This scheme works on arch linux nicely.

/etc/X11/Xwrapper.config is also setuped:

needs_root_rights = yes
allowed_users = anybody

Xwrapper does not work in nixos?

Nah, systemd runs as root. The process it launches from your user unit will be run as your user, though, hence the lack of root permissions. Otherwise any user could become root just by writing a user service - not exactly desirable.

Almost certainly not, editing files in /etc only works if the application in question actually reads from there. Often NixOS modules deliberately make it so applications don’t do that, because this is a potential source of impure behavior (i.e., deploying the same config on a different machine will make the application behave differently if there is a different, hand-written file in place).

That said, NixOS does not launch X with a setuid wrapper. You’re expected to use either startx or a display manager.

I thought the whole concept of starting X as root to enable some wonky semblance of systemd unit management at the cost of running a super complex process with root permissions (that is also known to be buggy and de-facto unmaintained) went the way of the dodo with logind and dbus improvements anyway?

Yes, my idea here is to get root Xorg via systemd-user via suid.

It seems xorg itself is compiled without suid/wrapper support in nixos. The description says that nixos source-based. If I try to build my xorg.xorg-server, won’t it force me to rebuild tons of packages every time, which is quite wasteful?

But the main reason is the management of x11-processes as services, this is necessary. It is expected that it should somehow pick up dbus/logind-related stuff.

Maybe. I’m not very familiar with lightdm but it looks too heavyweight at first glance. The approach I’ve been using all this time is good for kiosks or something like that, always maintains state

Depends. Packages that depend on xorg.xserver (and their dependants) specifically will need to be rebuilt. AIUI the xorg set of packages is quite fragmented, and things like libX11 are not in the critical path in this case, so the actual set of things that need to be rebuilt may be quite minimal.

If it’s not, upstream may accept a PR for having an option for switching on a setuid wrapper, at which point you could farm this out to hydra. Setuid binaries are anyway nontrivial on NixOS (because setuid in the nix store is problematic), this will need some tinkering on the module end.

If you do this for some commercial application and you don’t want to contribute upstream, you can set up a build farm whose cache kiosks grab their pre-built binaries from to do this efficiently. Someone needs to build the software, ultimately.

The industry sadly hasn’t come up with a good way to indicate ABI changes to allow culling dependency rebuild propagation yet, sadly. Even bazel struggles with this, despite its deep build knowledge, since it can’t predict runtime behavior.

I’d personally like to see a bit more justification of the use case - I’m sure there are other solutions to this than running X as root, between graphical-session.target and systemd-cat I can’t think of any use cases that can’t be covered by a user-owned Xorg.

Lightdm is also far from the only display manager, and for kiosks I believe you could escape to systemd-run or such if you have logged a user into a tty through autologin anyway, provided you absolutely need systemd to track the pid for some reason.

But then I’m not the one who would accept such a PR.