flakes/README.md
Chinmay D. Pai 400071a40a
docs: add builtwithnix badge
Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
2024-10-10 00:32:56 +05:30

14 KiB

NixOS Snowflake

Built with Nix

---

This repository contains modularized NixOS configurations, carefully compiled and in often places inspired by other similar configurations. The setup is flake-based and fully reproducible. Feel free to explore and use any part that inspires you.

Highlights

  • Modular flake setup based on Snowfall Lib
  • Secret management based on Agenix
  • Single flake setup for NixOS and Home Manager
  • Modules for servers, workstation, and mailserver
  • Encrypted BTRFS setup

Structure

The repository follows the standard Snowfall Lib flake structure:

.
├── checks
│   └── deploy-rs
├── homes
│   └── x86_64-linux
│       ├── chnmy@thonkpad
│       ├── server@bicboye
│       └── server@smolboye
├── lib
│   └── deploy-rs
├── modules
│   ├── home
│   │   ├── desktop
│   │   │   ├── firefox
│   │   │   └── wezterm
│   │   ├── development
│   │   │   ├── git
│   │   │   ├── helix
│   │   │   └── tmux
│   │   ├── shell
│   │   │   └── fish
│   │   └── user
│   └── nixos
│       ├── core
│       │   ├── docker
│       │   ├── fish
│       │   ├── gnupg
│       │   ├── lanzaboote
│       │   ├── nix
│       │   ├── security
│       │   └── sshd
│       ├── desktop
│       │   ├── fonts
│       │   ├── gnome
│       │   ├── hyprland
│       │   ├── kde
│       │   └── pipewire
│       ├── gaming
│       │   └── steam
│       ├── hardware
│       │   ├── bluetooth
│       │   ├── initrd-luks
│       │   └── yubico
│       ├── monitoring
│       │   ├── exporter
│       │   ├── grafana
│       │   └── victoriametrics
│       ├── networking
│       │   ├── mullvad
│       │   └── netbird
│       ├── services
│       │   ├── arr
│       │   ├── backup
│       │   ├── fail2ban
│       │   ├── gitea
│       │   ├── homebridge
│       │   ├── immich
│       │   ├── mailserver
│       │   ├── maych-in
│       │   ├── miniflux
│       │   ├── nginx
│       │   ├── ntfy-sh
│       │   ├── paperless
│       │   ├── postgresql
│       │   ├── unifi-controller
│       │   └── vaultwarden
│       └── user
├── overlays
│   ├── jellyfin-web
│   └── netbird
├── packages
│   └── vuetorrent
├── secrets
│   ├── machines
│   ├── monitoring
│   └── services
├── systems
│   └── x86_64-linux
│       ├── bicboye
│       ├── smolboye
│       └── thonkpad
├── flake.nix
└── data.nix
  • flake.nix: Entrypoint for the NixOS configurations
  • data.nix: Mappings for Agenix secrets, passed as specialArgs in flake.nix and referenced inside the configurations.
  • checks: Flake check configuration for deploy-rs
  • homes: Home configuration for the flakes, structured as <architecture>/<username>@<system>
  • lib: Custom library helper functions for the configuration, currently containing generator for deploy-rs
  • modules: Platform-based opinionated NixOS modules, containing home and nixos
    • home: Home Manager configuration options programs and services
    • nixos: NixOS configuration options for services, programs, and system setup
  • overlays: Customized nix package builds for existing packages
  • packages: Custom packages for NixOS
  • secrets: Agenix deployment secrets, referenced in data.nix

Modules

All modules are available under the modules/ directory, for both Home manager and NixOS, with the options listed under snowflake.*. These modules makes deploying complex configurations quite simple. For example: A full-fledged, minimal, KDE Plasma desktop can be enabled by adding snowflake.desktop.kde.enable = true to your system configuration.

The modular configuration also allows for efficient reusability and config de-duplication between machines. Most of the configuration is tailored to my needs, but is still highly-customizable except for the security defaults.

As an example, to deploy a new workstation with a desktop environment:

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

  hardware.cpu.intel.updateMicrocode = true;
  hardware.enableRedistributableFirmware = true;

  networking.hostName = "workstation";

  # Power management, enable powertop and thermald.
  powerManagement.powertop.enable = true;
  services.thermald.enable = true;

  snowflake = {
    stateVersion = "24.05";

    core.lanzaboote.enable = true;
    core.docker.enable = true;
    core.docker.storageDriver = "btrfs";

    desktop.enable = true;
    desktop.fingerprint.enable = true;
    desktop.kde.enable = true;

    hardware.bluetooth.enable = true;
    hardware.yubico.enable = true;

    networking.firewall.enable = true;
    networking.networkManager.enable = true;
    networking.iwd.enable = true;
    networking.resolved.enable = true;
    networking.netbird.enable = true;

    user.enable = true;
    user.username = "user";
    user.description = "User McUsername";
    user.extraGroups = ["video"];
    
    # NOTE: this requires adding an agenix secret to `secrets/secrets.nix`
    # and an entry in `data.nix` to work. Refer these files for more details.
    user.userPasswordAgeModule = userdata.secrets.machines.workstation.password;
    user.rootPasswordAgeModule = userdata.secrets.machines.workstation.root-password;
  }
};

Systems

  • thonkpad: Primary workstation on Lenovo X1 Carbon 12th Gen - Intel Core Ultra 7 155H, 32GB RAM
  • bicboye: A custom-built Homelab running Intel Core i5 12600K, 32GB RAM, 16TB storage
  • smolboye: Hetzner Cloud VPS Running a dual-core Intel Xeon, 4GB RAM

Secrets

For all deployment secrets, I am using agenix. All secrets are stored in the secrets/ directory and are encrypted with host SSH keys.

Adding secrets

To add new deployment secrets:

  • Create the relevant directory structure under secrets/ and add an entry to secrets.nix
  • Create/Edit the secret with agenix. This command needs to be run in the same directory as secrets.nix. You do not require agenix to be installed, and instead can use nix run, for example:
$ nix run github:ryantm/agenix#agenix -- -e services/service-name/password.age
  • Add an entry for the secret to data.nix. The added secret can then be used as userdata.secrets.services.service-name.password in the configuration. Refer to data.nix for more details.

Usage

NixOS Installation

This repository does not walk you through the entire installation process, but instead lists down steps to get an encrypted BTRFS partition setup for NixOS, mostly for future reference. So prepare a NixOS installation disk your preferred way, either by downloading the ISO, or using scripts like nixos-anywhere to bootstrap your system.

You may setup NixOS through either the traditional way, by manual partitioning, or through disko.

Traditional Partitioning

Disk Setup

Wipe the partition table with wipefs -a /dev/nvme0n1. Edit the partition table to create two partitions:

  • /dev/nvme0n1p1 - 512MiB EFI partition. This won't be encrypted.
  • /dev/nvmen01p2 - root partition, will use LUKS+BTRFS for root.

An encrypted swap partition can optionally be set up using BTRFS later.

BTRFS Setup

All commands prefixed with # are expected to be run as the root user. Make sure to enter a strong encryption password during luksFormat (and then remember it!).

# cryptsetup luksFormat --type=luk2 /dev/nvme0n1p2
# cryptsetup open /dev/nvme0n1p2 cryptroot
  • Format the EFI and the root partition:
# mkfs.fat -F32 /dev/nvme0n1p1
# mkfs.btrfs /dev/mapper/cryptroot
  • Create BTRFS subvolumes:
# mount /dev/mapper/cryptroot /mnt
# btrfs subvolume create /mnt/@
# btrfs subvolume create /mnt/@home
# btrfs subvolume create /mnt/@snapshots
# btrfs subvolume create /mnt/@log
# btrfs subvolume create /mnt/@cache
# btrfs subvolume create /mnt/@nix-config
# btrfs subvolume create /mnt/@nix-store
  • Create directories for the subvolumes:
# mkdir /mnt/@/home
# mkdir /mnt/@/.snapshots
# mkdir /mnt/@/boot
# mkdir /mnt/@/nix
# mkdir -p /mnt/@/var/log
# mkdir -p /mnt/@/var/cache
# mkdir -p /mnt/@/etc/nixos
# mkdir -p /mnt/@/nix

# umount -R /mnt
  • Mount all the file systems
# mount -o ssd,noatime,compress=zstd,space_cache=v2,autodefrag,subvol=@ /dev/mapper/cryptroot /mnt
# mount -o ssd,noatime,compress=zstd,space_cache=v2,autodefrag,subvol=@home,nodev /dev/mapper/cryptroot /mnt/home
# mount -o ssd,noatime,compress=zstd,space_cache=v2,autodefrag,subvol=@snapshots,nodev,nosuid,noexec /dev/mapper/cryptroot /mnt/.snapshots
# mount -o ssd,noatime,compress=zstd,space_cache=v2,autodefrag,subvol=@log,nodev,nosuid,noexec /dev/mapper/cryptroot /mnt/var/log
# mount -o ssd,noatime,compress=zstd,space_cache=v2,autodefrag,subvol=@cache,nodev,nosuid,noexec /dev/mapper/cryptroot /mnt/var/cache
# mount -o ssd,noatime,compress=zstd,space_cache=v2,autodefrag,subvol=@nix-config,nodev /dev/mapper/cryptroot /mnt/etc/nixos
# mount -o ssd,noatime,compress=zstd,space_cache=v2,autodefrag,subvol=@nix-store,nodev /dev/mapper/cryptroot /mnt/nix
# mount /dev/nvme0n1p1 /mnt/boot

Alternative Partitioning using Disko

Disko is a helper tool that lets you declaratively specify disk partitions for your system. This can be used as a replacement for the fileSystems.* option in NixOS. Check out the disko repository to set it up in flakes, or refer flake.nix for the setup.

Disko Configuration

A sample disko configuration for a VPS set up using nixos-anywhere:

# systems/x86_64-linux/smolboye/disk-config.nix
{
  disko.devices = {
    disk = {
      sda = {
        type = "disk";
        device = "/dev/sda";
        content = {
          type = "gpt";
          partitions = {
            boot = {
              priority = 1;
              name = "ESP";
              start = "1M";
              end = "128M";
              type = "EF00";
              content = {
                type = "filesystem";
                format = "vfat";
                mountpoint = "/boot";
              };
            };
            root = {
              size = "100%";
              content = {
                type = "btrfs";
                extraArgs = ["-f"];
                subvolumes = {
                  "/root" = {
                    mountpoint = "/";
                    mountOptions = ["compress=zstd" "noatime"];
                  };
                  "/home" = {
                    mountpoint = "/home";
                    mountOptions = ["compress=zstd" "noatime"];
                  };
                  "/nix" = {
                    mountpoint = "/nix";
                    mountOptions = ["compress=zstd" "noatime"];
                  };
                  "/swap" = {
                    mountpoint = "/.swapvol";
                    mountOptions = ["nodatacow" "noatime"];
                    swap.swapfile.size = "20M";
                  };
                  "/log" = {
                    mountpoint = "/var/log";
                    mountOptions = ["compress=zstd" "noatime"];
                  };
                };
              };
            };
          };
        };
      };
    };
  };
}

To partition the disks in live USB:

# nix --experimental-features "nix-command flakes" run github:nix-community/disko -- --mode disko ./systems/x86_64-linux/smolboye/disk-config.nix

Alternatively, running nixos-install should take care of the partitioning for you.

Installation

  • Clone the git repository
# git clone https://git.deku.moe/thunderbottom/flakes.git /mnt/etc/nixos
  • Get the UUID for the disks and edit hardware.nix to match the blkid output. This step can be skipped if you are using disko:
# blkid

/dev/nvme0n1p1: UUID="7FBB-9E80" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="b4e5f895-cd2d-4cc9-9878-03dc8fca178c"
/dev/nvme0n1p2: UUID="9de352ea-128f-4d56-a720-36d81dfd9b92" TYPE="crypto_LUKS" PARTUUID="95574dc5-5f5d-465d-b01c-f76918f7a125"
/dev/mapper/cryptroot: UUID="870fde90-a91a-4554-8b1c-d5702c789f4d" UUID_SUB="ccc90835-8b61-4d8e-8293-e1323a02fb3b" BLOCK_SIZE="4096" TYPE="btrfs"

If you are not using disko for partition setup, make sure to set the following values in hardware.nix:

  • luks.devices."cryptroot".device to /dev/nvme0n1p2 output, in this case: /dev/disk/by-uuid/9de352ea-128f-4d56-a720-36d81dfd9b92
  • All the values in fileSystems.*, except /boot to /dev/mapper/cryptroot output, /dev/disk/by-uuid/870fde90-a91a-4554-8b1c-d5702c789f4d
  • fileSystems."/boot" to /dev/nvme0n1p1 output, /dev/disk/by-uuid/7FBB-9E80

Check any of the hardware.nix files in the repository for more details.

  • Install NixOS with nixos-install and then reboot.

Deployments

deploy-rs

To deploy all hosts using deploy-rs:

$ deploy

To deploy a specific host:

$ deploy .#hostname

Traditional Nix Deployment

To deploy the traditional way using nix:

# nixos-rebuild switch --flake '.#hostname'

It is also possible to rebuild and deploy NixOS to a remote host. This can be used in cases where the remote host is incapable of building packages, or while running a VPS. To run remote deployments:

# nixos-rebuild switch --flake '.#hostname' --target-host user@ip --use-remote-sudo