Skip to content
mirkolenzPublic

About

Nix-based configuration for my machines

Topics

Resources

Stars

24 stars

Watchers

1 watching

Forks

Latest commit

 

History

6,611 Commits

Folders and files

Repository files navigation

Nix Setup

This repo contains flakectl, a wrapper to build, install, and update NixOS/nix-darwin/home-manager configurations:

nix run github:mirkolenz/infra -- --help

Repository Structure

This flake follows the dendritic pattern: every .nix file under modules/ is auto-loaded by import-tree (see flake.nix) and contributes to a flake.modules.<class>.<bucket> declaration, so the directory a file lives in is purely organizational. Feature modules are grouped by the config-path namespace they write, mirroring the option tree (and the options/ layout), so anything is found where it sits in the configuration. A module is one cohesive feature filed under one directory, the namespace of its primary subject, while keeping all of its config inline, so a feature like ssh or nix is configured end-to-end across home-manager and nixos/darwin in a single file.

NixOS Computers

Installation

# find device name and update this flake accordingly
ls -l /dev/disk/by-id
# format the disk
nix run github:mirkolenz/infra -- disko MACHINE_NAME --mode destroy,format,mount
# print hardware config and verify it is in sync with the flake
nixos-generate-config --no-filesystems --show-hardware-config
# set up user passwords
nix run github:mirkolenz/infra -- passwd --root /mnt MACHINE_NAME USER
# install the system
nix run github:mirkolenz/infra -- nixos-install MACHINE_NAME

Installer Swap

The NixOS manual recommends activating swap before nixos-install on machines with limited memory, since its builds may need quite a bit of RAM. Swap partitions declared via disko are already activated by the mount mode. Hosts with a swapfile can create a temporary one at its swapDevices path below /mnt after formatting the disk with disko. NixOS recreates it on the first boot. mkswap --file works on any filesystem and sets the nocow attribute required by btrfs.

mkswap --file /mnt/SWAPFILE --size 8G
swapon /mnt/SWAPFILE
# after nixos-install
swapoff /mnt/SWAPFILE
rm /mnt/SWAPFILE

Secure Boot

Lanzaboote replaces systemd-boot on UEFI hosts whose firmware can enroll custom keys via Setup Mode. This excludes all Macs, as pre-T2 models lack Secure Boot and T2 models only trust Apple and Microsoft. Replace boot.loader.systemd-boot.enable of the host with the following, modules/boot/default.nix provides the rest.

boot.lanzaboote = {
  enable = true;
  pkiBundle = "/var/lib/sbctl";
};
  1. Put the firmware into Setup Mode via "Reset to Setup Mode", not "Clear All Keys".
  2. Switch and reboot, the first boot generates the keys and reboots again to enroll them.
  3. Verify with bootctl status (Secure Boot: enabled (user)) and sudo sbctl verify.

To unlock a LUKS2 volume through the TPM2, also set boot.lanzaboote.measuredBoot.enable = true and add settings.crypttabExtraOpts = [ "tpm2-device=auto" ]; to its disko luks entry. Check that /run/current-system/systemd/lib/systemd/systemd-pcrlock is-supported prints yes, then enroll once after Secure Boot is active. Every switch afterwards updates the TPM policy, and the passphrase stays as recovery. Drop --tpm2-with-pin=true for unattended hosts.

sudo systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true --tpm2-pcrlock=/var/lib/systemd/pcrlock.json /dev/DEVICE

To revert, disable Secure Boot in the firmware before switching back to systemd-boot.

Troubleshooting

  • A warning about /boot being world-readable is not an issue, the permissions are correctly set after a reboot.
  • If you get an error like too many open files during nixos-install, try one of the following options:
    • Execute ulimit -n 65535 before installing to increase the number of open files for the current shell session
    • Use --max-jobs 2 to limit the number of parallel jobs (could also try with 1 max job)
  • If you get an error like Getting status of /nix/store/...: No such file or directory, try the following:
    nix-store --verify --check-contents --repair

Tailscale

  • Create an auth key, tagged tag:server for servers: https://login.tailscale.com/admin/settings/keys
  • Save it on the machine to ./tsauth.txt
  • Run sudo tailscale up --auth-key file:tsauth.txt && rm tsauth.txt
  • Run sudo systemctl restart tailscaled to reapply extraSetFlags

Mac Computers

Installation

  1. Install Apple Developer Tools: xcode-select --install
  2. Install Homebrew
  3. Install Nix
  4. Sign into the App Store
  5. Enable Full Disk Access for terminal application
  6. Sign into 1Password and enable Settings > Developer > Use the SSH agent
sudo nix run github:mirkolenz/infra
sudo reboot

Sync Configs to a Mac without Nix

For Macs that should not have Nix installed, the mirkos-macbook-rsync app copies a curated set of configuration files (Ghostty, SSH client, Homebrew bundle) from this flake to the remote machine via rsync over SSH. The list of files and the brew bundle invocation are derived directly from darwinConfigurations.mirkos-macbook, so they stay in sync with the nix-darwin configuration. Before running, make sure SSH key-based login to the remote works, see SSH Key Deployment.

Then sync the configs and apply any manual steps.

nix run .#mirkos-macbook-rsync -- USER@HOST

The files themselves live in system.build.remoteConfig (see modules/hosts/mirkos-macbook/remote-config.nix), a ~-rooted tree that the script builds on demand. To extend it with additional files, add an entry to the files attribute set there.

Uninstallation

chsh -s /bin/zsh
# if nix is not in PATH, use this: /nix/var/nix/profiles/default/bin/nix
sudo nix run github:mirkolenz/infra#darwin-uninstaller
sudo /nix/nix-installer uninstall

Applications and Settings

Shell
  • Copy $HOME/.local/share/fish/fish_history
System Preferences
  • General > About: Set name
  • Wallpaper: Solid color (black)
  • Appearance > Allow wallpaper tinting in windows: Off
  • Keyboard > Keyboard Shortcuts > Modifier Keys: Caps Lock -> Option
  • Keyboard > Keyboard Shortcuts > App Shortcuts: Disable all
  • Keyboard > Keyboard Shortcuts > Spotlight: Disable all
Raycast
  • Settings > Advanced > Import/Export (also set up new schedule)
  • Launch > Manage Fallback Command: Add Kagi
Zotero
  • Install Better BibTeX
  • Install Open PDF
  • Settings > General > File Renaming > {{ creators max="1" case="hyphen" }}-{{ year }}-{{ title truncate="50" case="hyphen" }}
  • Settings General > Reader > Tabs > Creator - Year - Title
  • Settings > Export > Item Format > Better BibTeX Citation Key Quick Copy
  • Settings > Better BibTeX > Citation Key Format > auth.capitalize + year + shorttitle(3,3)
  • Settings > Better BibTeX > Fields > Omit > file, keywords
Default Folder X
  • Settings > Options > Sync settings via iCloud
Arq Backup

Additional wildcard exclude rules:

.cache
.devenv
.direnv
.orbstack
.venv
__pycache__
node_modules
*/OrbStack
*/Library/CloudStorage
*/Library/Application Support/

Home-Manager Standalone

Note: Authorize your key as described in SSH Key Deployment and reconnect via SSH after installing nix.

sudo /nix/var/nix/profiles/default/bin/nix upgrade-nix
nix run github:mirkolenz/infra
sudo usermod -s $(which fish) "$USER"

Missing Locale

The configuration sets LC_ALL=en_US.UTF-8, and macOS forwards it via SSH, so the host has to provide this locale. Otherwise, its system shell warns setlocale: LC_ALL: cannot change locale (en_US.UTF-8) on login. Nix programs are not affected, since targets.genericLinux points them to the locale archive of Nix, so check with /usr/bin/locale -a instead. On Ubuntu, the following generates the locale without changing the system default.

sudo locale-gen en_US.UTF-8

Binary Cache

Determinate Nix overwrites /etc/nix/nix.conf on upgrades, so append the cache to the included /etc/nix/nix.custom.conf instead.

sudo vi /etc/nix/nix.custom.conf
sudo systemctl restart nix-daemon
extra-trusted-substituters = https://mirkolenz.cachix.org
extra-trusted-public-keys = mirkolenz.cachix.org-1:R0dgCJ93t33K/gncNbKgUdJzwgsYVXeExRsZNz5jpho=

Utilities and Snippets

GitHub Tokens

opnix renders two GitHub tokens from the item GitHub in the 1Password vault NixOS on the NixOS and macOS hosts that set services.onepassword-secrets.enable, other hosts get neither. The token of the agents is only rendered on hosts whose user sets programs.agents.enable. Use a single fine-grained token for public repositories or selected ones, with read-only access to Contents, Metadata, Actions, Pull requests, Issues, and Commit statuses.

  • The field nix-conf holds the access tokens of nix, which avoid rate limits, as the line access-tokens = github.com=github_pat_XXX. It lands in /var/lib/opnix/nix/secrets.conf on NixOS and /usr/local/var/opnix/nix/secrets.conf on macOS, owned by root and readable by the admin group, since the nix client fetches flakes as your user. nix skips the include while the file is missing or unreadable, as within the agent sandbox, where nono injects its content as NIX_CONFIG instead.
  • The field credential holds the bare token, which nono injects into the sandboxed agents as GH_TOKEN. It lands in /var/lib/opnix/agents/github-token on NixOS and /usr/local/var/opnix/agents/github-token on macOS, owned by your user with mode 0400. nono aborts the launch of an agent while the file is missing, so provision it before the first start.

The agent sandbox denies the file of the nix access tokens, and the nix daemon trusts no user but root, so nobody can hand the daemon other settings. Standalone home-manager hosts get neither token, their admin has to add an include to /etc/nix/nix.custom.conf.

To provision a host, create a 1Password service account with read access to the vault NixOS and store its token on every host. opnix token set reads the token from stdin, so it also works headless, e.g. sudo opnix token set < token.txt.

sudo opnix token set
# NixOS
sudo systemctl restart opnix-secrets
# macOS
sudo launchctl kickstart -k system/org.nixos.opnix-secrets

The service account token stays in /etc/opnix-token, readable by root only, since the group onepassword-secrets has no members.

S3 Binary Cache

flakectl is prepared to replace Cachix with an S3 bucket such as Cloudflare R2. With --cache s3://..., check-build pushes every target and its build closure, and gc-cache deletes everything not needed by the current targets of any system and older than 30 days. Switching requires the following steps:

  1. Create an R2 bucket, serve it publicly via a custom domain (e.g., cache.mirkolenz.com), and create an API token with write access.
  2. Generate a signing key with nix key generate-secret --key-name cache.mirkolenz.com-1 > key and derive its public half with nix key convert-secret-to-public < key.
  3. Add the secrets CACHE_SIGNING_KEY, CACHE_ACCESS_KEY_ID, and CACHE_SECRET_ACCESS_KEY as well as the variables CACHE_BUCKET and CACHE_ENDPOINT (https://<account-id>.r2.cloudflarestorage.com) to the repository.
  4. In flake.nix, replace the Cachix substituter and public key with the custom domain and the new public key.
  5. In checks.yaml, replace cachix-action with secret-key-files pointing to the signing key and run nix run . -- --cache "s3://$CACHE_BUCKET?endpoint=$CACHE_ENDPOINT&region=auto&compression=zstd" check-build with the secrets exported as AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. gc-cache reads them from the environment only, not from ~/.aws profiles.
  6. Add a job that runs gc-cache with the same arguments after check-build on pushes to main.
  7. Replace *.cachix.org in the agent sandbox domains with the custom domain.

Image Building

If building for another architecture on NixOS: Enable cross compiling

# for iso installer disc
nix build github:mirkolenz/infra#.legacyPackages.x86_64-linux.installer-default.iso-installer
# for apple t2
nix build github:mirkolenz/infra#.legacyPackages.x86_64-linux.installer-apple-t2.iso-installer

Flash the image to a USB drive or SD card with caligula:

sudo caligula burn --hash skip --compression auto result/iso/*.iso

Raspberry Pi

The SD card image holds the final raspi system, which then updates itself like every other host. Building it runs a VM, so the builder needs /dev/kvm. The root partition grows to the size of the card on first boot.

nix build github:mirkolenz/infra#nixosConfigurations.raspi.config.system.build.diskoImages
sudo caligula burn --hash skip --compression auto result/main.raw.zst

Every switch rewrites the boot code, U-Boot, and config.txt on /boot. The bootloader in the EEPROM is updated by hand, the update applies on the next reboot.

sudo rpi-eeprom-update -a
sudo reboot

Use MakeMKV

# otherwise the optical drive is not found: https://discourse.nixos.org/t/makemkv-cant-find-my-usb-blu-ray-drive/23714
sudo modprobe sg
# if that does not work, try again with sudo
nix run github:mirkolenz/infra#makemkv

Investigate ID Mappings

sudo podman run --rm --subuidname=$USER ubuntu cat /proc/self/uid_map

Password Hashing

Prompts for a password and writes its yescrypt hash to the users.users.USER.hashedPasswordFile that the configuration of MACHINE_NAME declares. If USER has a running GNOME session on this host, it also asks for the current password and re-encrypts the login keyring right away, so the keyring keeps unlocking at login.

sudo nix run github:mirkolenz/infra -- passwd MACHINE_NAME USER

Zellij Web Tokens

zellij web --create-token

SSH Key Generation

ssh-keygen -t ed25519 -a 128 -C "USER@HOST" -f ~/.ssh/id_ed25519

SSH Key Deployment

Keys live in 1Password (vault Mirko) and are offered by its SSH agent, so the private key never exists as a file. Long-lived keys additionally belong in custom.user.sshKeys (see modules/core/identity.nix) and in the agent config (see modules/programs/op.nix).

Since ssh-copy-id expects a local key file, copy the public key in 1Password and add it to the remote machine by hand instead. The following command logs in with the account password and opens the file in vim, where the key is pasted on its own line. -t allocates the terminal vim needs and umask makes both the directory and the file private.

ssh -t "USER@MACHINE_NAME" "umask 077; mkdir -p ~/.ssh; vim ~/.ssh/authorized_keys"

About

Nix-based configuration for my machines

Topics

Resources

Stars

24 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages