uranus

Custom-built desktop workstation.

uranus used to be a Windows gaming PC when I first built it in early 2022. After I started being interested in NixOS, I ran it on uranus for a bit via WSL. While this worked pretty well, it kind of left me wishing I was using NixOS for the whole machine. Eventually this impulse won and I wiped the machine and set it up with NixOS. It's gotten a few upgrades since:

The GPU upgrade was pretty small in terms of performance, but really I was intentionally moving from NVIDIA to AMD, since the later is better supported in Linux. While I did see NVIDIA get better while I was still using it, I wasn't willing to wait for it to be good enough.

NixOS configuration

{
  pkgs,
  lib,
  ...
}:
{
  imports = [
    ./hardware-configuration.nix
  ];

  networking.hostName = "uranus";
  services.openssh.enable = true;
  system.stateVersion = "24.05";

  <<nixos-config>>
}
mjm.desktop.enable = true;
mjm.desktop.niri.enable = true;

Since it's a workstation, it uses my own desktop module with all of the desktop configuration. I'm using niri on all of my workstations.

mjm.secureboot.enable = true;

uranus uses my secureboot module to set up Lanzaboote, which enables SecureBoot and measured boot on NixOS systems. I use this to avoid needing to unlock the encrypted filesystem on boot: as long as the boot configuration hasn't been tampered with, it will be unlocked automatically.

uranus uses a disk setup of LUKS -> LVM -> btrfs. Because it's using LUKS, it can take advantage of systemd-cryptenroll's support for unlocking the disk with the TPM based on the pcrlock policy from measured boot.

boot.kernelPackages = pkgs.linuxPackages_latest;

Workstations generally benefit from having the latest kernel changes.

services.hardware.bolt.enable = true;

This option enables managing access to Thunderbolt devices, which sometimes require approval before they can actually be used.

networking.networkmanager.enable = false;

The mjm.desktop module will normally enable NetworkManager, but because uranus is a desktop that is always plugged in to Ethernet in the same location, I disable that so it will use networkd instead.

services.kmscon.config.font-dpi = 192;

uranus has pretty high resolution displays, so I've configured the console DPI to be a bit higher than default so it's actually readable.

nix.settings = {
  max-jobs = 4;
  cores = 8;
};

The default settings for Nix will aggressively overcommit resources in most cases. Both the number of concurrent jobs and the number of cores each job can use are set based on the number of cores in the machine. With several highly parallel builds, this can easily pin down a machine to the point of insane contention, or it can easily exhaust memory. It's usually a good idea to tune these down, as builds will often complete faster this way.

nixpkgs.config.permittedInsecurePackages = [ "electron-39.8.10" ];

My workstations install Bitwarden Desktop to use with my Vaultwarden install. Unfortunately, Bitwarden Desktop is using an end-of-life version of Electron, so I have to allow that insecure package. This nixpkgs config key doesn't merge well across multiple modules, so I keep a single list at the host level instead.

specialisation.plasma.configuration = {
  mjm.desktop.niri.enable = lib.mkForce false;
  mjm.desktop.plasma.enable = true;
};

I keep around a specialisation of the system that uses Plasma 6 instead of niri.

Wireplumber configuration

services.pipewire.wireplumber.extraConfig.desk-devices = {
  "context.properties" = {
    "log.level" = "I";
  };

  "monitor.alsa.rules" = [
    <<alsa-rules>>
  ];
};

I have some extra configuration for Wireplumber on this machine because otherwise it ends up having a bunch of options for devices that I'm never going to want to use. With this config, I can clean that up!

I also enable info level logging. At once point, I needed to use some more complex logic than what's here, so I needed to write Lua scripts for Wireplumber, and the extra logging was helpful for figuring that out.

{
  matches = [
    { "api.alsa.card.name" = "HDA ATI HDMI"; }
    { "api.alsa.card.name" = "HD-Audio Generic"; }
  ];
  actions.update-props."device.disabled" = true;
}

This rule disables entire devices. "HDA ATI HDMI" is actually the outputs from the DisplayPort connections on my graphics card, but I have an Apple Studio Display that I use for speakers, and those speakers show up as their own device, so I don't need to ever send audio through those DisplayPort connections. Similarly, "HD-Audio Generic" is the device for the audio connections on my motherboard, which I also don't use.

{
  matches = [
    {
      "media.class" = "Audio/Sink";
      "api.alsa.card.name" = "Yeti Stereo Microphone";
    }
    {
      "media.class" = "Audio/Source";
      "api.alsa.card.name" = "Studio Display";
    }
  ];
  actions.update-props."node.disabled" = true;
}

This rule disables individual nodes. I have some devices that function as both source and sink, but I only use them for one of those. The Yeti microphone can also output audio I guess, but I'm not going to use it for that. And the Apple Studio Display has a microphone, but I have a dedicated microphone, so I'm not going to want to use that either. By disabling those nodes, I clean up the visible options and reduce the chances that the wrong one is ever selected.

Home configuration

{
  <<home-config>>
}
home.sessionVariables.DIPPY_EVAL_CONCURRENCY = "4";

By default, my deploy tool, dippy, only evaluates two jobs at once, to avoid using too much RAM. Since uranus has quite a bit of RAM, I configure it to evaluate four jobs at once.

xdg.configFile."niri/config.local.kdl".text = ''
  output "Dell Inc. DELL U2723QE 5FN18P3" {
    transform "270"
    scale 2.0
  }
'';

At my desk, I have two displays. The primary is an Apple Studio Display, as at the time (and maybe still) it was somehow the most practical 5K display available. The secondary display is a Dell 4K 27" display, which is rotated to be in portrait orientation. This line drops an optional config file into place that my Niri config reads, which lets it know that display should be rotated. It also sets the scale to an even 2x, as I believe Niri will default to a fractional scale based on its resolution.

Proxy Information
Original URL
gemini://midna.dev/homelab/hosts/uranus/
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
35.395933 milliseconds
Gemini-to-HTML Time
0.834776 milliseconds

This content has been proxied by September (UNKNO).