Nix

// Declaritive organization

Modules

  • A nix function (expression really) with some auto-injected parameters. So a specific structure. Almost feels like a standard library?
  • Can “module bust” and imports.
  • When to module vs. flake? A module is a lower-level primitive to break up code.
  • Manual.
{
  imports = [
    # Paths to other modules.
  ];

  options = {
    # Option declarations.
  };

  config = {
    # Option definitions.
  };
}   

Module structure.

  • By convention, modules are functions which toss back the structure. The input attribute set usually has at least config, pkgs. So takes an attribute set and returns one.
  • imports is a special property. The module system implicitly passes args to them. That is why you don’t see explicit calls with arguements.
    • config which is the final value of the resulting configuration after applying all modules.
    • pkgs which represents the set of packages.
    • lib which has some helper functions.
    • whatever got specified in any module through _module.args.
  • options defines namespaces which can be set higher up.
  • Might make sense to have system and user modules.

Derivations

A runtime with conventions to build packages.

NixPkgs

A big monorepo of package derivations similar to the AUR. Can be treated as one big flake or a branch as a channel.

Channels

Pretty much a git branch name. Just tries to provide a hash of packages which work nice together.

Flakes

  • A layer over configuration for my deterministic builds.
  • Install software packages from other flake sources, which is much more flexible than installing directly from nixpkgs.
  • Channels vs. Flakes? Sounds like the older channels are more limited, focused just on package updates. Flakes can handle that and more, so don’t bother with channels or nix-env.
    • Channels seem more centralized, a git repo, kinda like the AUR.
  • NixPkgs can be provided to a system as a channel or as a flake. The choice is basically yours. Most of the Nix ecosystem is available either way now.

Previous to Flakes, you basically set up Nix so that it had a global map of everything it could do, and NixPkgs was that map. With Flakes, everyone can create their own local pieces of Nix and they can be attached to other local pieces of the map. It takes things from centralized to distributed, safely and with full reproducibility. This may or may not be something you care about, and it’s fine either way.

  • Flakes aren’t real.
  • Has a new set of commands, less -.
  • Checking in a lock file forces the exact version of inputs and the script.

commands

  • nix flake update
    • Update the flake! Pull in new versions to the lock file.
    • --commit-lock-file – not sure when relevant?
  • nix flake metadata

NixOS

  • Flakes give distributed power and maybe a better allocation of responsibility since the maintainers themseleves manage flake instead of nixpkg indirection.

first boot

The bare minimum to get things booting, the idea being once it boots it can be rolled back or fixed. Boot to USB. Disable secure boot first since the boot drive won’t have the required credentials. The NixOS GUI is pretty slick, maybe good enough for me.

partitioning and encrypting

The “Erase” option in the GUI makes it easy, creates the standard 2 partitions and LUKS2 encrypts the main one. The hardware configuration looks to be generated into `` which may make it easier to manage.

bootloader and hardware

Looks like systemd is not required, but it is the default. NixOS sets PID1 to a systemd. The generated configuration.nix contains some boot.loader settings. Not a lot to setup here.

networking

  • Need to install static lease on router for server.
  • Update any Unbound DNS for local LAN.
  • Enable openssh daemon if useful for server.

flake-ify

  1. Add git package in configuration and then enable flakes:
nix.settings.experimental-features = [ "nix-command" "flakes" ];

System setting

  1. Rebuild.
  2. Add flake.nix with the template.
  3. Rebuild.
sudo nixos-rebuild switch --flake .#gemini2

Sometimes requires being explicit about the host.

dts

  • Get an SSH key and add it to gemini3.
ssh-keygen -t rsa -b 4096
ssh-copy-id user@server_ip  

configuration

  • /etc/nixos/flake.nix – Entry for configuration so that inputs are locked. I think best to still push as much as possible into configuration.nix though.
  • /etc/nixos/configuration.nix – System configuration. All the declarative configuration for the system, including settings for the time zone, language, keyboard layout, network, users, file system, and boot options.
  • /etc/nixos/hardware-configuration.nix – Generated for the local machine. A nix module.
  • Search for packages at search.nixos.org.
    • environment.systemPackages is the key in the configuration.

flake.nix/flake.lock, configuration.nix, and hardware-configuration.nix are the system specific configuration. Probably want to track flake and configuration and not hardware since that is generated. Shared modules maybe separate?

programs and services

Higher level constructs that tie things together. Compare to grabbing package and configuring yourself. Services are for daemons.

commands

  • nixos-rebuild switch
    • THE command to build a configuration and switch the system to it.
    • Looks for a flake.nix if present, can also be passed a flake with --flake.
    • Need to first update flake, nix flake update, if want to pull in new versions of nixpkgs.
    • I don’t think it will update the kernel (since that is “in memory”). Looks like systemd services can be hooked up to restart with restartIfChanged.

Development Shells

nix develop <flake>

  • nix develop... is part of the new nix command, which is eating up all the nix-* commands.
    • nix develop creates a shell with all buildInputs and environment variables of a derivation loaded and shellHooks executed.
    • The closely related nix shell... is meant of just apps, not dependencies of the app.
  • By default, a lock file gets created next to the flake.nix location, not the calling location. Can use the --impure flag to have the lock file stored in central .config location.
  • To integrate into a shell without creating a whole new shell like nix develop, can run nix print-dev-env, this is what direnv does under the hood. But kinda hard to manage.
  • Can integrate into repositories which don’t have direnv + nix support with local .git/info/exclude. A .gitignore which is not tracked. The syntax for both files is the same.
.envrc  
.direnv/
result

.git/info/exclude

Home Manager

To manage user-level configuration in the Home directory, you need Home Manager. But I am not sure if this is worth it. Is it that much better than a dotfiles repo?

Install instructions provide some clarity on what this is exactly. Standalone separates the system from the user’s home, whereas module connects them in lockstep. Sounds like it might be merged in to the main codebase some day.

Install

  • home-manager.url = "github:nix-community/home-manager"; – Adds the flake from github.

Package Install Conventions

  • If you want a software package to continue working when you switch to the root user, or if you want a configuration to apply system-wide, you should install it using NixOS Modules.
  • Home Manager for everything else to keep things scope’d.