Hi! I apologize in advance for the length; I wanted to provide what I felt was adequate context. I’m relatively new to NixOS.
In preparation for revamping my bare-metal home server, I’ve been building out a few config files, in a VM. I think I’m in the final stages, and am ironing out the file that will handle part of my storage configuration.
I’ll probably add it as storage.nix
, since hardware-configuration.nix
says it shouldn’t be modified. However, for now, it’s all in hardware-configuration.nix
.
For context, I have 5 drives, 3 of which will be part of my mass storage, and relevant to this post:
- 1 × WD 18 TB HDD, for parity
- 2 × Seagate 14 TB HDD, for data
- The other 2 are a couple NVMe SSDs—1 for boot, 1 for root & home
I hope to have everything encrypted with LUKS, but am open to not doing so if it’ll be a bad idea. I just know I might have sensitive data (like photos) on the HDDs.
Below, you’ll see my config, as it exists right now. Everything before ## Drives
and after ".AppleDB"
was generated by the NixOS install in the VM. Everything in-between was written by me.
Below that, you’ll see what I think the config might look like when all’s said and done.
hardware-configuration.nix
as it currently exists:
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "sr_mod" "virtio_blk" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/36ea268c-fbec-45b8-8a46-bd29d8666171";
fsType = "ext4";
};
boot.initrd.luks.devices."luks-22dc2fb9-e4f6-4985-8456-de7fee20b938".device = "/dev/disk/by-uuid/22dc2fb9-e4f6-4985-8456-de7fee20b938";
swapDevices =
[ { device = "/dev/disk/by-uuid/d7c62d37-fd47-4339-88a7-1d9ef5fd377b"; }
];
## Drives
############
# NVMe drive(s) for Boot (Hope to pool in the future, but probably won't.)
## nvme-Kingston_SSD_KC3000_1TB_SERIAL
#
# NVMe drive(s) for Root and Home (Hope to pool in the future, but probably won't.)
## nvme-Kingston_SSD_KC3000_2TB_SERIAL
#
# Parity
## Parity01 - ata-WDBAMA0180HBK-NESN-XA_SERIAL (DCM: WGBPZCM)
#
# Data pool
## Disk01 - ata-STKP14000400-3EGAP6-571_SERIAL (DOM: 10/2023)
## Disk02 - ata-STKP14000400-3EGAP6-570_SERIAL (DOM: 03/2023)
# Media storage disks, pooled by MergerFS
fileSystems."/mnt/jbod" =
{ device = "/mnt/disks/disk*";
fsType = "mergerfs";
options = ["defaults" "minfreespace=250G" "fsname=mergerfs-jbod"];
};
fileSystems."/mnt/disks/disk01" =
{ device = "/dev/disk/by-id/ata-STKP14000400-3EGAP6-571_SERIAL";
fsType = "xfs";
};
fileSystems."/mnt/disks/disk02" =
{ device = "/dev/disk/by-id/ata-STKP14000400-3EGAP6-570_SERIAL";
fsType = "xfs";
};
# SnapRAID
services.snapraid = {
enable = true;
#extraConfig = ''
# nohidden
# blocksize 256
# hashsize 16
# autosave 500
# pool /pool
#'';
parityFiles = [
# Defines the file(s) to use as parity storage
# It must NOT be in a data disk
# Format: "FILE_PATH"
"/mnt/disks/parity01/snapraid.parity"
];
contentFiles = [
# Defines the files to use as content list
# You can use multiple specification to store more copies
# You must have least one copy for each parity file plus one. Some more don't
# hurt
# They can be in the disks used for data, parity or boot,
# but each file must be in a different disk
# Format: "content FILE_PATH"
"/var/snapraid.content"
"/mnt/disks/parity/parity01/.snapraid.content"
"/mnt/disks/disk01/.snapraid.content"
"/mnt/disks/disk02/.snapraid.content"
];
dataDisks = {
# Defines the data disks to use
# The order is relevant for parity, do not change it
# Format: "DISK_NAME DISK_MOUNT_POINT"
d01 = "/mnt/disks/disk01/";
d02 = "/mnt/disks/disk02/";
};
#touchBeforeSync = true; # Whether `snapraid touch` should be run before `snapraid sync`. Default: true.
sync.interval = "03:00";
scrub.interval = "weekly";
#scrub.plan = 8; # Percent of the array that should be checked by `snapraid scrub`. Default: 8.
#scrub.olderThan = 10; # Number of days since data was last scrubbed before it can be scrubbed again. Default: 10
exclude = [
# Defines files and directories to exclude
# Remember that all the paths are relative at the mount points
# Format: "FILE"
# Format: "DIR/"
# Format: "/PATH/FILE"
# Format: "/PATH/DIR/"
"*.unrecoverable"
"/tmp/"
"/lost+found/"
"*.!sync"
".AppleDouble"
"._AppleDouble"
".DS_Store"
"._.DS_Store"
".Thumbs.db"
".fseventsd"
".Spotlight-V100"
".TemporaryItems"
".Trashes"
".AppleDB"
];
};
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
What I think it might look like in the end. Again, the parts not auto-generated will be in their own config file.
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
# Removed `imports` and `boot.` for relevance / brevity.
fileSystems."/" =
{ device = "/dev/disk/by-uuid/UUID";
fsType = "btrfs"; # Because I can't be bothered with ZFS right now.
};
fileSystems."/home" =
{ device = "/dev/disk/by-uuid/UUID";
fsType = "btrfs"; # Because I can't be bothered with ZFS right now.
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/2AD5-541F";
fsType = "vfat";
};
boot.initrd.luks.devices."luks-UUID".device = "/dev/disk/by-uuid/UUID";
# Need to figure out how to set swappiness.
swapDevices = [ {
device = "/var/lib/swapfile";
size = 16*1024;
} ];
## Drives
############
# NVMe drive(s) for Boot (Hope to pool in the future, but probably won't.)
## nvme-Kingston_SSD_KC3000_1TB_SERIAL
#
# NVMe drive(s) for Root and Home (Hope to pool in the future, but probably won't.)
## nvme-Kingston_SSD_KC3000_2TB_SERIAL
#
# Parity
## Parity01 - ata-WDBAMA0180HBK-NESN-XA_SERIAL (DCM: WGBPZCM)
#
# Data pool
## Disk01 - ata-STKP14000400-3EGAP6-571_SERIAL (DOM: 10/2023)
## Disk02 - ata-STKP14000400-3EGAP6-570_SERIAL (DOM: 03/2023)
# Media storage disks, pooled by MergerFS
fileSystems."/mnt/jbod" =
{ device = "/mnt/disks/disk*";
fsType = "mergerfs";
options = ["defaults" "minfreespace=250G" "fsname=mergerfs-jbod"];
};
fileSystems."/mnt/disks/disk01" =
{ device = "/dev/disk/by-id/ata-STKP14000400-3EGAP6-571_SERIAL";
fsType = "xfs";
};
fileSystems."/mnt/disks/disk02" =
{ device = "/dev/disk/by-id/ata-STKP14000400-3EGAP6-570_SERIAL";
fsType = "xfs";
};
# SnapRAID
services.snapraid = {
enable = true;
#extraConfig = ''
# nohidden
# blocksize 256
# hashsize 16
# autosave 500
# pool /pool
#'';
parityFiles = [
# Defines the file(s) to use as parity storage
# It must NOT be in a data disk
# Format: "FILE_PATH"
"/mnt/disks/parity01/snapraid.parity"
];
contentFiles = [
# Defines the files to use as content list
# You can use multiple specification to store more copies
# You must have least one copy for each parity file plus one. Some more don't
# hurt
# They can be in the disks used for data, parity or boot,
# but each file must be in a different disk
# Format: "content FILE_PATH"
"/var/snapraid.content"
"/mnt/disks/parity/parity01/.snapraid.content"
"/mnt/disks/disk01/.snapraid.content"
"/mnt/disks/disk02/.snapraid.content"
];
dataDisks = {
# Defines the data disks to use
# The order is relevant for parity, do not change it
# Format: "DISK_NAME DISK_MOUNT_POINT"
d01 = "/mnt/disks/disk01/";
d02 = "/mnt/disks/disk02/";
};
#touchBeforeSync = true; # Whether `snapraid touch` should be run before `snapraid sync`. Default: true.
sync.interval = "03:00";
scrub.interval = "weekly";
#scrub.plan = 8; # Percent of the array that should be checked by `snapraid scrub`. Default: 8.
#scrub.olderThan = 10; # Number of days since data was last scrubbed before it can be scrubbed again. Default: 10
exclude = [
# Defines files and directories to exclude
# Remember that all the paths are relative at the mount points
# Format: "FILE"
# Format: "DIR/"
# Format: "/PATH/FILE"
# Format: "/PATH/DIR/"
"*.unrecoverable"
"/tmp/"
"/lost+found/"
"*.!sync"
".AppleDouble"
"._AppleDouble"
".DS_Store"
"._.DS_Store"
".Thumbs.db"
".fseventsd"
".Spotlight-V100"
".TemporaryItems"
".Trashes"
".AppleDB"
];
};
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
Any feedback you can provide on my config may be helpful. I feel like I’m almost at the finish line, where prep is concerned, but the configuration of my filesystem(s) feels like one of the last major hurdles before I can install and move on to declaring Home Manager and Docker/Podman.
Some resources I used: