NixOS config for workstations

{
  lib,
  config,
  pkgs,
  ...
}:
let
  cfg = config.mjm.desktop;
in
{
  imports = [
    ./autologin.nix
    ./boot.nix
    ./chrysalis.nix
    ./firefox.nix
    ./fonts.nix
    ./games.nix
    ./network.nix
    ./niri.nix
    ./plasma.nix
    ./sound.nix
    ./ssh.nix
    ./ssh-tpm.nix
    ./virtualisation.nix
  ];

  options.mjm.desktop = {
    enable = lib.mkEnableOption "desktop environment";
  };

  config = lib.mkIf cfg.enable {
    <<config>>
  };

  _class = "nixos";
}

I have three different NixOS workstations, and they all use the same overall configuration for their desktops. It's big enough to be broken down into several submodules.

=> autologin
=> firefox
=> games
=> network
=> niri
=> ssh
=> ssh-tpm

TODO document the other submodules

Config

The options set here in the main file are kind of a grab bag of small things that don't make sense to split into other files.

mjm.helix.enable = lib.mkDefault true;

My desktop machines also automatically enable my module for the Helix text editor.

documentation.enable = true;

In the base module, I disable documentation to save space and build time. But it's useful, so I reenable it on workstations.

time.timeZone = "America/Denver";

Workstations should show the local time rather than UTC.

It would probably make sense to set this to null on laptops, since they can move between time zones. Setting the time zone imperatively might make more sense there.

mjm.deploy.targetHost = lib.mkOverride 900 null;

Workstations should be excluded from automated deploys, which is done by setting their target host to null. The override makes it take precedence over mkDefault, but still be lower priority than a normal setting of the option.

services.displayManager.plasma-login-manager.enable = true;

Regardless of desktop environment or compositor, I am using plasma-login-manager as the display manager. It's the evolution of sddm, and based on my experience it is the least janky one. In particular, NixOS does some pretty weird things to set up gdm, so I'm avoiding that.

services.displayManager.hiddenUsers = [ "matt" ];

When I first set up several of my machines, I used the username "matt", but now I use "mjm" across the board. Because I'm using userborn to set up users and groups, deleting a user once it is created is not trivial. This old user is inactive, but without this configuration, it will still show up in login prompts.

systemd.oomd = {
  enableRootSlice = true;
  enableUserSlices = true;
};

systemd-oomd is enabled by default on NixOS, but it doesn't take action unless you configure slices to act based on memory pressure. These options enable the same defaults Fedora uses, which will kill processes in the root slice and user slices when they exceed 80% memory pressure for 20 seconds.

hardware.bluetooth.enable = true;
services.blueman.enable = true;

All of my workstations are capable of using Bluetooth (uranus needs a dongle) so enable it across the board.

services.printing.enable = true;
services.system-config-printer.enable = true;
services.avahi.enable = true;

Much to my dismay, there is in fact a printer in my home. It's an evil thing, but when it wants to cooperate, it is nice to be able to use it.

programs.kdeconnect.enable = true;

It's nice to be able to use KDE Connect to get notifications from my phone or send text messages from my computer. Installing the package also configures the kdeconnectd backend service to autostart with the desktop session.

services.udev.extraRules = ''
  ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c24a", ATTR{power/wakeup}="enabled"
'';

For some reason, the Logitech G600 mouse that I use when gaming isn't able to wake up the system by default. I don't have this problem with my Kinesis Advantage 2 keyboard or my Kensington Trackball, which are my primary peripherals at my desk. Maybe it's connected to the wrong port or something. This udev rule fixes the issue. I keep it in the desktop module in the event that I ever plug another machine into the displays at my desk.

programs.nix-ld.enable = true;

nix-ld makes it possible to run binaries on NixOS that expect libraries to be in more normal FHS locations. I avoided it for a long time, but eventually caved due to issues with compiling Zed extensions that this fixes.

boot.binfmt.emulatedSystems =
  let
    inherit (pkgs.stdenv.hostPlatform) system;
  in
  lib.mkMerge [
    (lib.mkIf (system == "x86_64-linux") [ "aarch64-linux" ])
    (lib.mkIf (system == "aarch64-linux") [ "x86_64-linux" ])
  ];

It's really easy to make a NixOS machine be able to build and run binaries for another architecture. It uses qemu-user, which means it's by no means fast, but it can be useful in a pinch, especially when you manage machines for multiple architectures. Since I have both x86_64 and aarch64 machines, I configure my workstations to emulate whichever architecture they are not.

environment.systemPackages = lib.attrValues {
  inherit (pkgs)
    bitwarden-desktop
    element-desktop
    jellyfin-mpv-shim
    krita
    lagrange
    mpv
    # radicle-desktop
    signal-desktop
    strawberry
    wl-clipboard
    xclip
    xdg-utils
    yt-dlp
    zeal
    ;
  inherit (pkgs.kdePackages)
    ark
    filelight
    kcalc
    ktorrent
    okular
    plasmatube
    ;

  homestuck = lib.mkIf pkgs.stdenv.hostPlatform.isx86_64 pkgs.unofficial-homestuck-collection;
};

These are miscellaneous packages that are installed on all of my desktop machines.

environment.etc."yt-dlp/plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins".source =
  "${pkgs.python314Packages.bgutil-ytdlp-pot-provider}/lib/python3.14/site-packages/yt_dlp_plugins";
environment.etc."yt-dlp/config".text = ''
  -v
  -t mkv
  --extractor-args youtubepot-bgutilscript:server_home=${pkgs.python314Packages.bgutil-ytdlp-pot-provider}/share/bgutil-ytdlp-pot-provider
  --cookies-from-browser=firefox
'';

This yt-dlp plugin helps avoid issues downloading videos from YouTube, which I like to do sometimes for archival purposes.

environment.etc."mpv/mpv.conf".text = ''
  video-sync = display-resample
  profile = high-quality
'';

I have some basic configuration for mpv which I sometimes use to watch videos.

environment.etc."xdg/user-dirs.defaults".text = ''
  DESKTOP=desktop
  DOCUMENTS=documents
  DOWNLOAD=downloads
  PICTURES=pictures
  PROJECTS=src
'';

I prefer to make some customizations to the default XDG user dirs. I remove some of the directories that I don't personally use, and others are lowercased or (in the case of Projects) renamed.

Proxy Information
Original URL
gemini://midna.dev/homelab/modules/nixos/desktop/
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
20.860053 milliseconds
Gemini-to-HTML Time
0.537022 milliseconds

This content has been proxied by September (UNKNO).