Declare the disk layout with disko
Installing used to mean six manual steps through Calamares, one of which
copied hardware-configuration.nix into the repo — a step that fails
silently unless the file is also git added, since Nix only sees tracked
files. It also did not scale past one machine.
modules/disk.nix defines moonarch.disk.{enable,device,encrypt,espSize,
swapSize} and builds disko.devices from it, so another machine is a
device path rather than a new layout. Installing is a single
disko-install run from the ISO.
hardware-configuration.nix is no longer needed: nvme, ahci, sd_mod and
the USB HID modules are in the initrd via
boot.initrd.includeDefaultModules, and nixpkgs.hostPlatform comes from
nixosSystem. The testvm copy described a qcow2 that no longer exists and
is deleted; that host now uses the module against /dev/vda.
The user password no longer arrives with the installer, so it is set
once via nixos-enter after installing instead of committing a hash.
This commit is contained in:
1 parent
9988dc1e7b
commit
1ec12da9ff
7 files changed
+279
-81
No files matched your search
@@ -0,0 +1,127 @@
|
||||
# ABOUTME: Declarative disk layout for every host: GPT, optional LUKS2, btrfs.
|
||||
# ABOUTME: A host sets moonarch.disk.device; the layout itself is shared.
|
||||
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.moonarch.disk;
|
||||
|
||||
mountOptions = [ "compress=zstd" "noatime" ];
|
||||
|
||||
subvolumes = {
|
||||
"/root" = { mountpoint = "/"; inherit mountOptions; };
|
||||
"/home" = { mountpoint = "/home"; inherit mountOptions; };
|
||||
# Separate so the store stays out of the snapper snapshots of /. It is
|
||||
# reproducible from the flake and would only inflate them.
|
||||
"/nix" = { mountpoint = "/nix"; inherit mountOptions; };
|
||||
} // lib.optionalAttrs (cfg.swapSize != "") {
|
||||
# No compression or noatime here: disko creates the swapfile inside this
|
||||
# subvolume, and btrfs requires it to be nodatacow, which it sets itself.
|
||||
"/swap" = {
|
||||
mountpoint = "/.swapvol";
|
||||
swap.swapfile.size = cfg.swapSize;
|
||||
};
|
||||
};
|
||||
|
||||
btrfs = {
|
||||
type = "btrfs";
|
||||
# Overwrite an existing signature instead of asking. The device is erased
|
||||
# by this point either way.
|
||||
extraArgs = [ "-f" ];
|
||||
inherit subvolumes;
|
||||
};
|
||||
|
||||
# Neither passwordFile nor settings.keyFile is set, which makes disko's
|
||||
# askPassword default to true: it prompts for the passphrase twice while
|
||||
# formatting and never puts it on a command line.
|
||||
luks = {
|
||||
type = "luks";
|
||||
name = "crypted";
|
||||
# Lets TRIM reach the SSD through the container. The tradeoff is that the
|
||||
# pattern of used blocks becomes visible on the raw device.
|
||||
settings.allowDiscards = true;
|
||||
content = btrfs;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.moonarch.disk = {
|
||||
enable = lib.mkEnableOption "the declarative disk layout";
|
||||
|
||||
device = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "/dev/disk/by-id/nvme-eui.0123456789abcdef";
|
||||
description = ''
|
||||
Whole disk to partition, as a /dev/disk/by-id/ path — that one survives
|
||||
enumeration changes, unlike /dev/nvme0n1.
|
||||
|
||||
This value decides which disk gets erased. Check it against lsblk on the
|
||||
target machine before installing.
|
||||
|
||||
Only the partitioning reads it. The generated fileSystems entries go
|
||||
through /dev/disk/by-partlabel/, so a booted system does not depend on
|
||||
the path staying the same.
|
||||
'';
|
||||
};
|
||||
|
||||
encrypt = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Put the filesystem inside a LUKS2 container. The layout is otherwise
|
||||
identical, so a machine that does not need encryption — a throwaway VM —
|
||||
gets the same subvolumes.
|
||||
'';
|
||||
};
|
||||
|
||||
espSize = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "1G";
|
||||
description = ''
|
||||
Size of the EFI system partition. Every boot entry keeps its kernel and
|
||||
initrd here, so this scales with the number of generations kept.
|
||||
'';
|
||||
};
|
||||
|
||||
swapSize = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "8G";
|
||||
example = "";
|
||||
description = ''
|
||||
Size of the swapfile in the /.swapvol subvolume. Empty string creates no
|
||||
swap at all.
|
||||
|
||||
Sized as a safety valve, not as a replacement for RAM. Hibernation needs
|
||||
at least as much swap as RAM plus a resume_offset kernel parameter, which
|
||||
is not set up here — see DECISIONS.md.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
disko.devices.disk.main = {
|
||||
type = "disk";
|
||||
device = cfg.device;
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
ESP = {
|
||||
size = cfg.espSize;
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
mountOptions = [ "umask=0077" ];
|
||||
};
|
||||
};
|
||||
# Named the same whether or not it is encrypted, so the partlabel the
|
||||
# fileSystems entries point at does not depend on cfg.encrypt.
|
||||
root = {
|
||||
size = "100%";
|
||||
content = if cfg.encrypt then luks else btrfs;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user