This module includes configuration that is automatically enabled for any NixOS system I use.
Unlike most of my modules, there's no "mjm.foo" option to enable the behavior.
{
config,
lib,
inputs,
...
}:
{
imports = [
"${inputs.home-manager}/nixos"
../../common/base
<<nixos-submodules>>
];
config = {
<<nixos-misc>>
};
_class = "nixos";
}This module extends the common base modules for all machines (not just NixOS):
=> common/base: Defaults for all machines
./users.nix
{
pkgs,
lib,
config,
...
}:
{
<<nixos-users-config>>
_class = "nixos";
}This module contains configuration related to Unix users and permissions.
services.userborn.enable = true; services.userborn.passwordFilesLocation = "/var/lib/nixos";
I'm using userborn on all of my systems to manage users instead of the default Perl script that NixOS uses. It helps get interpreters out of the core functionality of booting the system, and is generally just more predictable than the default user management.
users.mutableUsers = false;
Disabling mutable users is a more natural fit. In my opinion, this should be the default, but I suppose it's a compromise to those more used to other Unix systems. With this setting, only users declared in the NixOS config will exist: add users at runtime is not possible.
users.manageLingering = false;
I don't use lingering for any users, so I'd rather not have the mechanism present for managing it.
security.sudo.enable = false; security.run0.enable = true; security.run0.wheelNeedsPassword = false; security.run0.enableSudoAlias = true; security.pam.services.systemd-run0.startSession = lib.mkForce false;
I use run0 instead of sudo on all of my systems. This avoids another setuid binary, closing off a class of vulnerabilities exposed by sudo.
users.users.mjm = {
isNormalUser = true;
description = "MJ";
extraGroups = [ "wheel" ];
shell = pkgs.fish;
hashedPassword = "$y$j9T$tM/RKSjlb5ljgtpGT/Y8N1$3oXxWQh/q.KKCcJKoyVeIUVqjjt76EWX.uNEJRASt04";
};
nix.settings.trusted-users = [ "mjm" ];
boot.initrd.systemd.emergencyAccess = config.users.users.mjm.hashedPassword;I have one normal user defined on my machines. I use the fish shell, and I'm in the wheel group so that I can easily administer the system. For a similar reason, I add my user to the list of trusted users for nix. In particular, this avoids issues around trusting artifacts that I copy to servers via SSH.
./kmscon.nix
{
pkgs,
config,
lib,
...
}:
{
<<kmscon-config>>
_class = "nixos";
}I've been using kmscon on my machines where possible to get a nicer console experience.
services.kmscon = {
enable = lib.mkDefault true;
config = {
font-name = "PragmataPro Liga";
font-size = lib.mkDefault 16;
mouse = true;
hwaccel = config.hardware.graphics.enable;
xkb-options = "ctrl:nocaps";
palette = "custom";
palette-black = "73,77,100";
palette-red = "237,135,150";
palette-green = "166,218,149";
palette-yellow = "238,212,159";
palette-blue = "138,173,244";
palette-magenta = "245,189,230";
palette-cyan = "139,213,202";
palette-light-grey = "184,192,224";
palette-dark-grey = "91,96,120";
palette-light-red = "237,135,150";
palette-light-green = "166,218,149";
palette-light-yellow = "238,212,159";
palette-light-blue = "138,173,244";
palette-light-magenta = "245,189,230";
palette-light-cyan = "139,213,202";
palette-white = "165,173,203";
palette-foreground = "202,211,245";
palette-background = "36,39,58";
};
};I default to enabling kmscon for a nicer console experience.
fonts.packages = lib.mkIf config.services.kmscon.enable [ pkgs.pragmata-pro ];
Above I set the font to use for kmscon, but that only works if the font is actually installed. That will be true already for systems that use my desktop module, but for servers that don't use that, they need it added here.
./chrony.nix
{ lib, ... }: {
services.timesyncd.enable = lib.mkDefault false;
services.chrony.enable = lib.mkDefault true;
services.chrony.extraFlags = [ "-s" ];
_class = "nixos";
}I'm using chrony as an NTP client rather than systemd-timesyncd. The latter only supports Simple NTP, which is missing important features of ordinary NTP.
The -s flag is important for the Raspberry Pis, since they don't have a hardware clock. The flag causes chrony to remember the time from the last time it was running, which brings it closer to correct between boots.
=> chrony FAQ: Should I prefer chrony over timesyncd if I do not need to run a server?
./asahi.nix
{
inputs,
lib,
config,
...
}:
{
imports = [
"${inputs.nixos-apple-silicon}/apple-silicon-support"
];
config = lib.mkMerge [
{ hardware.asahi.enable = lib.mkDefault false; }
(lib.mkIf config.hardware.asahi.enable {
<<asahi-config>>
})
];
_class = "nixos";
}The NixOS module from the nixos-apple-silicon project is gated behind the hardware.asahi.enable option. It currently defaults to true, but that will change in the future to support this situation, where it can be imported unconditionally and then enabled as desired.
hardware.asahi.extractPeripheralFirmware = config.hardware.asahi.peripheralFirmwareDirectory != null;
Asahi Linux makes use of some firmware blobs that it extracts on the macOS side when installing and stores on the EFI partition. These then need to be gathered up and extracted and exposed to Linux. These blobs are proprietary, so legally they needed to be loaded off the actual system they were pulled from: they can't be redistributed from somewhere else.
This poses a small hiccup when building the system in CI to check for build errors. That firmware directory on the ESP is not available, so extracting the firmware is not possible. To workaround that, I just disable the extraction when the directory can't be found. When building and applying locally, the extraction will happen as it needs to, but otherwise, it can still build the rest of the system without issue.
services.chrony.enableRTCTrimming = false; services.chrony.extraConfig = '' rtcsync '';
With the default config for chrony, you'll see this error on an Asahi system:
Could not enable RTC interrupt : Invalid argument
This will prevent chrony from being able to update the RTC, so it may drift. Even worse, if the machine's battery dies, it will get reset to the Unix epoch, and never get fixed (except perhaps if you boot into macOS). Disabling RTC trimming and instead using the "rtcsync" directive does not require RTC interrupts, so chrony is able to update the RTC without issue.
./catppuccin.nix
{ inputs, pkgs, ... }:
{
imports = [
"${inputs.catppuccin}/modules/nixos"
];
catppuccin.enable = true;
catppuccin.autoEnable = false;
catppuccin.flavor = "macchiato";
<<catppuccin-sources>>
_class = "nixos";
}I use catppuccin-nix to set up color schemes for many different things. I enable the module globally, but set autoEnable to false, so no catppuccin theming should be enabled that isn't explicitly mentioned in my configs. As for flavor, I prefer macchiato: dark, but not too dark.
catppuccin.sources = (import inputs.catppuccin { inherit pkgs; }).packages.overrideScope (
_: _: {
whiskers = pkgs.catppuccin-whiskers;
}
);Unfortunately, catppuccin-nix makes some use of IFD (import-from-derivation) to run the whiskers tool at evaluation time to produce some of the color themes. whiskers is a Rust tool, and Rust is not known for its fast compilation times. Needing to build whiskers can really slow things down, especially when building a system for another architecture using qemu-user. To make things worse, it doesn't end up getting cached in my personal attic cache because it doesn't end up in the closure of the final system.
The workaround for this is to override the catppuccin-nix module to pull whiskers from Nixpkgs. That version should be cached already.
time.timeZone = lib.mkDefault "Etc/UTC";
I want most of my machines to use UTC as the time zone. It makes more sense for servers to not have that kind of ambiguity around time. So I set it by default. Desktop machines will override this to my actual time zone.
system.etc.overlay.enable = true;
I'm using the experimental option to mount /etc as an overlay filesystem. In this mode, NixOS builds an erofs image for the contents of /etc, and then allows for writes via an overlayfs.
This option also makes it possible to make /etc immutable if desired, but I haven't put in the effort to make sure my machines don't actually need that.
programs.command-not-found.enable = false;
The common base module sets up nix-index, which provides its own command-not-found functionality. The default one provided by NixOS only works if the programs sqlite DB is present in the pkgs path, which is not the case if using a Git archive. So I disable it across the board in favor of nix-index.
documentation.enable = lib.mkDefault false;
I disable building documentation for servers and VMs, as it can sometimes be slow or broken. I can usually look up a man page on the workstation I'm using instead, as the desktop module will re-enable this.
networking.nftables.enable = lib.mkDefault true;
nftables is easier to work with than iptables, so I enable that by default. In some cases, it needs to be disabled due to a NixOS service module only supporting the iptables backend for NixOS's firewall, in which case I'll reluctantly re-enable it for just that machine.
nix.channel.enable = false;
I don't use nix-channel nor nixos-rebuild, so the paths that this manages are not needed for me.
system.disableInstallerTools = lib.mkDefault true;
This option is marked internal, with a description claiming to use at your own risk. That said, I have my own tooling for setting up my NixOS systems, so I don't actually use any of the things this installs, so I might as well leave it off and avoid any churn, especially in VMs, that comes from having them.
boot.loader.timeout = 0;
Removing the boot loader timeout of course makes booting faster, but it means the menu doesn't show at all. Thankfully, you can just hold down Space while booting to deactivate the timeout and use the menu.
zramSwap.enable = lib.mkDefault (!config.microvm.guest.enable && !config.boot.zswap.enable);
I avoid doing fancy swap things on microVMs. I used to enable zram swap on all other machines, but I've come to learn that's maybe not a good idea a lot of the time. My impression now is that in most cases, a swap partition with zswap enabled gives more desirable performance characteristics when under memory pressure. So I've been moving to that configuration where I can (i.e. on systems where I have a swap partition already or the ability to create one).
So now I'm left with a configuration where zram swap is enabled by default unless a machine has opted in to zswap.
text/gemini;lang=en-USThis content has been proxied by September (UNKNO).