Disko Integration

Disko provides a declarative way to partition and format disks with nix.

This module provides some pre-tested disko examples, which can be imported and re-used across hosts.

Most of the disko profiles only define root disk setups.

Module Options Reference for provision.fs.disko

Usage

You can use preconfigured disko profiles in your NixOS configurations:

bcachefs is supported, but should definitely treated as experimental, I have had a few issues / errors with it, and have been able to recover so far, but I do not recommend storing any important data with it. Stick to ZFS for important data.

Simple unencrypted btrfs

Unencrypted btrfs with GPT with ESP (UEFI support), vfat /boot partition.

Uses btrfs-simple-uefi

provision.fs = {
  btrfs.enable = true; # enable extra tools etc.
  disko.devices.root = {
    device = "/dev/nvme0n1"; # set device to install root on
    profile = "btrfs-simple-uefi";
    # `btrfs-simple-uefi` supports `extraDatasets` to extend profile's datasets
    args.extraDatasets = {
      "@lib" = {
        mountpoint = "/var/lib";
        mountOptions = ["compress=zstd"];
      };
    };
  };
};

Native encrypted bcachefs root

Native encrypted bcachefs with GPT with ESP (UEFI support), unencrypted vfat /boot.

Uses bcachefs-encrypted-uefi

provision.fs = {
  btrfs.enable = true; # enable extra tools etc.
  disko.devices.nvme = {
    profile = "bcachefs-encrypted-uefi";
    device = "/dev/disk/by-id/nvme-Samsung_SSD_970_PRO_1TB_SERIAL_NUMBER";
    args.rootName = "bcachefs";
  };
};

LUKS encrypted Ext4 root

Luks encrypted / Ext4 with GPT with ESP (UEFI support), unencrypted vfat /boot.

Uses ext4-luks-bios-uefi

provision.fs.disko.devices.root = {
  device = "/dev/sda";
  profile = "ext4-luks-bios-uefi";
  args.bootEnd = "1G"; # args can be used to override arguments from the profile
  args.iter-time = 10000;
};

Profiles

Below is a reference of the current disko profiles.

Ext4 + UEFI: ext4-simple-uefi

  • GPT partition table
  • /boot: vfat (ESP)
  • /: Ext4
# included from ./ext4-simple-uefi.nix
{
  device ? "/dev/vda",
  diskName ? "root",
  bootSize ? "1G",
  ...
}:
{
  disko.devices.disk.${diskName} = {
    inherit device;
    type = "disk";
    content = {
      type = "gpt";
      partitions = {
        ESP = {
          type = "EF00";
          size = bootSize;
          content = {
            type = "filesystem";
            format = "vfat";
            mountpoint = "/boot";
          };
        };
        root = {
          size = "100%";
          content = {
            type = "filesystem";
            format = "ext4";
            mountpoint = "/";
          };
        };
      };
    };
  };
}

Ext4 + UEFI + BIOS: ext4-simple-bios-uefi

  • GPT partition table
  • Grub MBR partition
  • /boot: vfat (ESP)
  • /: Ext4
# included from ./ext4-simple-bios-uefi.nix
{
  device ? "/dev/mmcblk0",
  diskName ? "main",
  bootStart ? "1M",
  bootEnd ? "1G",
  luksSize ? "100%",
  ...
}:
{
  disko.devices.disk.${diskName} = {
    type = "disk";
    inherit device;
    content = {
      type = "gpt";
      partitions = {
        boot = {
          priority = 1;
          start = "0";
          end = bootStart;
          type = "EF02"; # for grub MBR
        };
        ESP = {
          priority = 2;
          start = bootStart;
          end = bootEnd;
          type = "EF00";
          content = {
            type = "filesystem";
            format = "vfat";
            mountpoint = "/boot";
          };
        };
        root = {
          start = bootEnd;
          end = luksSize;
          content = {
            type = "filesystem";
            format = "ext4";
            mountpoint = "/";
          };
        };
      };
    };
  };
}

Ext4 + LUKS + UEFI + BIOS: ext4-luks-bios-uefi

  • GPT partition table
  • Grub MBR partition
  • /boot: vfat (ESP)
  • /: LUKS encrypted Ext4
# included from ./ext4-luks-bios-uefi.nix
{
  device ? "/dev/vda",
  diskName ? "root",
  bootEnd ? "350M",
  luksEnd ? "100%",
  iter-time ? 3000, # in ms
  key-size ? 512,
  cipher ? "aes-xts-plain64",
  ...
}:
let
  root = {
    type = "filesystem";
    format = "ext4";
    mountpoint = "/";
  };

  # luks container in partition 2
  crypted-root = {
    type = "luks";
    name = "crypted-root";
    extraOpenArgs = [ "--allow-discards" ];
    # this is expected to be present at boot
    # settings.keyFile = "/tmp/root-luks.key";
    settings.allowDiscards = true;
    extraFormatArgs = [
      "--iter-time ${toString iter-time}"
      "--hash sha256"
      "--cipher ${cipher}"
      "--key-size ${toString key-size}"
    ];
    # required using na-install script during nixos-anywhere installation
    # when using luks
    passwordFile = "/tmp/root-luks.key";
    content = root;
  };
in
{
  ## GPT Bios Compatible
  disko.devices.disk.${diskName} = {
    type = "disk";
    inherit device;
    content = {
      type = "gpt";
      partitions = {
        boot = {
          name = "boot";
          size = "1M";
          type = "EF02";
        };
        esp = {
          name = "ESP";
          size = bootEnd;
          type = "EF00";
          content = {
            type = "filesystem";
            format = "vfat";
            mountpoint = "/boot";
          };
        };
        luks = {
          name = "luks";
          size = luksEnd;
          content = crypted-root;
        };
      };
    };
  };
}

Bcachefs + native encryption + UEFI: bcachefs-encrypted-uefi

  • GPT partition table
  • /boot: vfat (ESP)
  • /: Bcachefs (native encryption)
# included from ./bcachefs-encrypted-uefi.nix
{
  device ? "/dev/vda",
  diskName ? "nvme",
  rootName ? "root",
  # is somewhat for compat
  bootEnd ? "1G",
  luksSize ? "100%",
  encrypted ? true,
  compression ? "zstd",
  # set to empty to skip
  acl ? true,
  discard ? true,
  lib,
  ...
}:
let
  bootStart = "1MiB";
in
{
  disko.devices.disk.${diskName} = {
    type = "disk";
    inherit device;
    content = {
      type = "gpt";
      partitions = {
        ESP = {
          priority = 2;
          start = bootStart;
          end = bootEnd;
          type = "EF00";
          content = {
            type = "filesystem";
            format = "vfat";
            mountpoint = "/boot";
          };
        };
        root = {
          name = rootName;
          size = luksSize;
          content = {
            type = "filesystem";
            format = "bcachefs";
            extraArgs = lib.flatten [
              (lib.optionalString encrypted "--encrypted")
              (lib.optionalString (compression != "") "--compression=${compression}")
              (lib.optionalString acl "--acl")
              (lib.optionalString discard "--discard")
            ];
            mountpoint = "/";
          };
        };
      };
    };
  };
}

Bcachefs + LUKS + UEFI: bcachefs-luks-uefi

  • GPT partition table
  • /boot: vfat (ESP)
  • /: LUKS encrypted bcachefs
# included from ./bcachefs-luks-uefi.nix
{
  device ? "/dev/vda",
  diskName ? "nvme",
  rootName ? "crypted-root",
  # is somewhat for compat
  bootEnd ? "1G",
  luksSize ? "100%",
  luksName ? "luks",
  # name of luks container
  # luks opts
  keyFile ? "",
  # if set, this key file is expected at boot i.g. "/tmp/root-luks.key"
  passwordFile ? "",
  # during nixos-anywhere install, this file on the installing host to use as a LUKS passphrase
  iterTime ? 10000,
  # 10s iter time
  hash ? "sha256",
  cipher ? "aes-xts-plain64",
  keySize ? 512,
  useRandom ? true,
  # bcachefs opts
  compression ? "zstd",
  # set to empty to skip
  acl ? true,
  discard ? true,
  lib,
  ...
}:
let
  inherit (lib) mkIf optional;
  bootStart = "1M";
  root = {
    type = "filesystem";
    format = "bcachefs";
    mountpoint = "/";
    extraArgs = lib.flatten [
      (lib.optionalString (compression != "") "--compression=${compression}")
      (lib.optionalString acl "--acl")
      (lib.optionalString discard "--discard")
    ];
  };
in
{
  ## GPT Bios Compatible UEFI
  disko.devices.disk.${diskName} = {
    type = "disk";
    inherit device;
    content = {
      type = "gpt";
      partitions = {
        boot = {
          priority = 1;
          size = bootStart;
          type = "EF02";
        };
        ESP = {
          priority = 2;
          size = bootEnd;
          type = "EF00";
          content = {
            type = "filesystem";
            format = "vfat";
            mountpoint = "/boot";
          };
        };
        ${luksName} = {
          size = luksSize;
          content = {
            type = "luks";
            name = rootName;
            # extraOpenArgs = [ "--allow-discards" ];
            # this is expected to be present at boot
            settings.keyFile = mkIf (keyFile != "") keyFile;
            settings.allowDiscards = mkIf discard true;
            extraFormatArgs = [
              "--iter-time ${toString iterTime}"
              "--hash ${hash}"
              "--cipher ${cipher}"
              "--key-size ${toString keySize}"
            ] ++ (optional useRandom "--use-random");
            # required using na-install script during nixos-anywhere installation
            # when using luks
            passwordFile = mkIf (passwordFile != "") passwordFile;
            content = root;
          };
        };
      };
    };
  };
}

ZFS mirror + LUKS: zfs-mirror-luks

  • 2 disks
  • not designed as root FS
  • GPT partition tables
  • /<pool>: LUKS with ZFS on mirrored drives
# included from ./zfs-mirror-luks.nix
{
  disks ? [
    "/dev/vda1"
    "/dev/vda2"
  ],
  ## LUKS options
  # LUKS password file
  passwordFile ? "/tmp/secret.key",
  # LUKS extra format arguments
  extraFormatArgs ? [
    "--iter-time 10000"
    "--hash sha512"
    "--cipher aes-xts-plain64"
    "--key-size 512"
  ],
  ## ZFS Options
  pool ? "mymirror",
  mountpoint ? "/${pool}",
  # ZFS options
  options ? {
    ashift = "13";
    autotrim = "on";
  },
  # ZFS root FS options
  rootFsOptions ? {
    compression = "zstd";
    "com.sun:auto-snapshot" = "false";
    recordsize = "1M";
    xattr = "sa";
    relatime = "on";
    acltype = "posixacl";
    dnodesize = "auto";
  },
  datasets ? {
    mydataset = {
      type = "zfs_fs";
      mountpoint = "/${pool}/mydataset";
    };
  },
  ...
}:
{
  disko.devices = {
    ## ZFS Pool Setup
    zpool.${pool} = {
      type = "zpool";
      mode = "mirror";
      inherit
        datasets
        mountpoint
        options
        rootFsOptions
        ;
    };

    ## Physical Disk Layout
    disk = {
      "${pool}-disk0" = {
        type = "disk";
        device = builtins.elemAt disks 0;
        content = {
          inherit passwordFile extraFormatArgs;
          type = "luks";
          name = "${pool}-crypted0";
          content = {
            inherit pool;
            type = "zfs";
          };
        };
      };
      "${pool}-disk1" = {
        type = "disk";
        device = builtins.elemAt disks 1;
        content = {
          inherit passwordFile extraFormatArgs;
          type = "luks";
          name = "${pool}-crypted1";
          content = {
            inherit pool;
            type = "zfs";
          };
        };
      };
    };
  };
}

Btrfs + UEFI: btrfs-simple-uefi

  • GPT partition table
  • /boot: vfat (ESP)
  • /: btrfs
# included from ./btrfs-simple-uefi.nix
{
  device ? "/dev/vda",
  diskName ? "root",
  bootStart ? "1M",
  bootSize ? "1G",
  # btrfs opts
  extraDatasets ? {
    # allows overriding default subvolume layout
    "@snapshots" = {
      mountpoint = "/snapshots";
      mountOptions = [
        "compress=zstd"
        "noatime"
      ];
    };
    "@containers" = {
      mountpoint = "/containers";
      mountOptions = [ "noatime" ];
    };
  },
  ...
}:
let
  # btrfs filesystem inside luks container
  btrfs = {
    type = "btrfs";
    extraArgs = [ "--label nixos" ];
    subvolumes = {
      "@" = {
        mountpoint = "/";
        mountOptions = [ "noatime" ];
      };
      "@nix" = {
        mountpoint = "/nix";
        mountOptions = [
          "compress=zstd"
          "noatime"
        ];
      };
      "@home" = {
        mountpoint = "/home";
        mountOptions = [
          "compress=zstd"
          "noatime"
        ];
      };
      "@log" = {
        mountpoint = "/var/log";
        mountOptions = [ "noatime" ];
      };
    } // extraDatasets;
  };
in
{
  disko.devices.disk.${diskName} = {
    inherit device;
    type = "disk";
    content = {
      type = "gpt";
      partitions = {
        ESP = {
          type = "EF00";
          size = bootSize;
          content = {
            type = "filesystem";
            format = "vfat";
            mountpoint = "/boot";
          };
        };
        root = {
          size = "100%";
          content = btrfs;
        };
      };
    };
  };
}

Btrfs + LUKS + UEFI: btrfs-luks-uefi

  • GPT partition table
  • /boot: vfat (ESP)
  • /: LUKS encrypted btrfs
# included from ./btrfs-luks-uefi.nix
{
  device ? "/dev/vda",
  diskName ? "root",
  rootName ? "crypted-root",
  bootStart ? "1M",
  bootEnd ? "1G",
  luksSize ? "100%",
  luksName ? "luks",
  # name of luks container
  # luks opts
  keyFile ? "",
  # if set, this key file is expected at boot i.g. "/tmp/root-luks.key"
  passwordFile ? "",
  # during nixos-anywhere install, this file on the installing host to use as a LUKS passphrase
  iterTime ? 10000,
  # 10s iter time
  hash ? "sha256",
  cipher ? "aes-xts-plain64",
  keySize ? 512,
  useRandom ? true,
  discard ? true,
  # btrfs opts
  extraDatasets ? {
    "@snapshots" = {
      mountpoint = "/snapshots";
      mountOptions = [
        "compress=zstd"
        "noatime"
      ];
    };
    "@containers" = {
      mountpoint = "/containers";
      mountOptions = [ "noatime" ];
    };
  },
  lib,
  ...
}:
let
  inherit (lib) mkIf optional;
  # btrfs filesystem inside luks container
  root = {
    type = "btrfs";
    extraArgs = [ "--label nixos" ];
    subvolumes = {
      "@" = {
        mountpoint = "/";
        mountOptions = [ "noatime" ];
      };
      "@nix" = {
        mountpoint = "/nix";
        mountOptions = [
          "compress=zstd"
          "noatime"
        ];
      };
      "@home" = {
        mountpoint = "/home";
        mountOptions = [
          "compress=zstd"
          "noatime"
        ];
      };
      "@log" = {
        mountpoint = "/var/log";
        mountOptions = [ "noatime" ];
      };
    } // extraDatasets;
  };
in
{
  disko.devices.disk.${diskName} = {
    type = "disk";
    inherit device;
    content = {
      type = "gpt";
      partitions = {
        ESP = {
          priority = 2;
          start = bootStart;
          end = bootEnd;
          type = "EF00";
          content = {
            type = "filesystem";
            format = "vfat";
            mountpoint = "/boot";
          };
        };
        ${luksName} = {
          size = luksSize;
          content = {
            type = "luks";
            name = rootName;
            # extraOpenArgs = [ "--allow-discards" ];
            # this is expected to be present at boot
            settings.keyFile = mkIf (keyFile != "") keyFile;
            settings.allowDiscards = mkIf discard true;
            extraFormatArgs = [
              "--iter-time ${toString iterTime}"
              "--hash ${hash}"
              "--cipher ${cipher}"
              "--key-size ${toString keySize}"
            ] ++ (optional useRandom "--use-random");
            # required using na-install script during nixos-anywhere installation
            # when using luks
            passwordFile = mkIf (passwordFile != "") passwordFile;
            content = root;
          };
        };
      };
    };
  };
}