This commit is contained in:
Anish Lakhwara
2026-01-19 22:37:30 -08:00
parent 3b33575b2a
commit d0cde973e7
21 changed files with 818 additions and 243 deletions
+52 -62
View File
@@ -1,79 +1,73 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running nixos-help).
# and in the NixOS manual (accessible by running 'nixos-help').
{ config, pkgs, ... }:
{
imports =
[
# Include the results of the hardware scan.
./hardware-configuration.nix
];
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
./disko.nix
];
# No systemd emergency mode (can't reliably be accessed over SSH)
systemd.enableEmergencyMode = false;
# Use the GRUB 2 boot loader.
boot.loader.efi.canTouchEfiVariables = false;
boot.loader.efi.efiSysMountPoint = "/boot/efi";
boot.loader.grub = {
enable = true;
device = "nodev";
efiSupport = true;
enableCryptodisk = true;
efiInstallAsRemovable = true;
# ZFS requires a hostId
networking.hostId = "bb7d707a";
# Boot configuration for LUKS + ZFS
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.efi.efiSysMountPoint = "/boot";
boot.loader.systemd-boot.enable = true;
# ZFS support
boot.supportedFilesystems = [ "zfs" ];
boot.zfs = {
requestEncryptionCredentials = [ "tank" ]; # Load key for tank pool
forceImportRoot = false;
};
boot.initrd.secrets = {
"/keyfile0.bin" = /etc/secrets/initrd/keyfile0.bin;
"/keyfile1.bin" = /etc/secrets/initrd/keyfile1.bin;
};
# Remote SSH unlock
boot.initrd.network.enable = true;
boot.initrd.network.ssh = {
enable = true;
port = 22;
authorizedKeys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDM0Zvei46x/yZl/IeBCq6+IYQQ0avulzVyBysF9cPigZMCybWRV7IEU+E3k9t6JrbdbdGfJkcZIWmsWDdKS8W8mBnZpVoT0ffLynu8JQ/TKdGm4Qv6bgUeKNrGsNv0ZPs2CDaGSLj0oJfRF7Ko10tcLP0vW+yujrh+y6TH/vVzJioaV4TGvtCUpn+wEQah9ROwPQLUUofsSWdnRsDJ/gp37zXWs4l5wyjSKtP3O9RZUP7kBekbSqEgSXiTk0oUQSVqIWl9NDiP6onk/gSOjXsR/JPqsSN/XI/c/yj6gyY0f51Ru2D7iBxuMJIJcWV+rU6coIj+ULcQWLzt/7TI8jq5AOOzI/ll4zbL24Eo84Rz+TP9tvMMhDZ0VaMN22AJ8qQEjc5P09tWKsX7Jg39XelyV1jHXncE4yvIE9F4RSCHzWCeKeXakizQNuzSaxTxIExRFYHjNW5bR6+3MTGwVrEIXU+qML+0yFTR86MT+tdY5AreAJQLwbog79O1NupeXJE= anish@curve " ];
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; # create this file with `ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key`
};
boot.initrd.availableKernelModules = [ "igc" "iwlwifi" ];
boot.initrd.luks.devices = {
"root" = {
#name = "root";
device = "/dev/disk/by-uuid/f37f3222-47d7-42d8-b400-363320a31853"; # UUID for /dev/nvme01np2
preLVM = true;
allowDiscards = true;
keyFile = "/keyfile0.bin";
# ZFS services
services.zfs = {
autoScrub = {
enable = true;
interval = "weekly";
};
autoSnapshot = {
enable = true;
frequent = 4; # 15-minute snapshots
hourly = 24;
daily = 7;
weekly = 4;
monthly = 12;
};
trim.enable = true;
};
# Data mount
# fileSystems."/data" = {
# device = "/dev/disk/by-uuid/3276a297-9ee4-4998-b262-1ed100366c06"; # UUID for /dev/mapper/crypted-data
# encrypted = {
# enable = true;
# label = "crypted-data";
# blkDev = "/dev/disk/by-uuid/8a317bf4-fe13-4334-a6df-5fe5a5048b5e"; # UUID for /dev/sda1
# keyFile = "/keyfile1.bin";
# };
# };
networking.interfaces.enp2s0 = {
ipv4.addresses = [{
address = "192.168.1.240";
prefixLength = 24;
}];
ipv6.addresses = [{
address = "fd7d:587a:4300:1::240";
prefixLength = 64;
}];
ipv4.routes = [{ address = "192.168.1.0"; prefixLength = 24; via = "192.168.1.1"; }];
ipv4.addresses = [
{
address = "192.168.1.240";
prefixLength = 24;
}
];
ipv6.addresses = [
{
address = "fd7d:587a:4300:1::240";
prefixLength = 64;
}
];
ipv4.routes = [
{
address = "192.168.1.0";
prefixLength = 24;
via = "192.168.1.1";
}
];
useDHCP = false;
};
#networking.nameservers = [ "172.16.11.240" ];
networking.nameservers = [ "192.168.1.1" ];
networking.defaultGateway = {
address = "192.168.1.1";
@@ -81,11 +75,8 @@
};
networking.hostName = "box"; # Define your hostname.
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
# Per-interface useDHCP will be mandatory in the future, so this generated config
# replicates the default behaviour.
networking.useDHCP = false;
networking.interfaces.wlp3s0.useDHCP = true;
@@ -103,4 +94,3 @@
system.stateVersion = "19.09"; # Did you read the comment?
}
+36 -30
View File
@@ -1,4 +1,5 @@
{ self, pkgs, ... }: {
{ self, pkgs, ... }:
{
imports = [
./configuration.nix
../profiles/core
@@ -6,10 +7,10 @@
../profiles/taskd
../profiles/shaarli
../profiles/dns
# ../profiles/monitoring
../profiles/monitoring
../profiles/nfs
../profiles/gonic
../profiles/headphones
../profiles/headphones
../profiles/radicale
# ../profiles/seafile # waiting for https://github.com/NixOS/nixpkgs/pull/249523 to be merged
../profiles/syncthing
@@ -19,64 +20,69 @@
../profiles/finance
../profiles/sync/website
../profiles/sync/music
../profiles/grasp
# ../profiles/archivebox
# ../profiles/woodpecker-agent
# ../profiles/jellyfin
# ../profiles/grasp # private repo - disabled
# ../profiles/archivebox # requires insecure django - fix in flake.nix permittedInsecurePackages
../profiles/woodpecker-agent
../profiles/jellyfin
../profiles/ulogger-server
../profiles/immich
../profiles/jacket
../profiles/gpodder
../profiles/transmission
../profiles/raven
#../profiles/postgres_upgrade_script
# ../profiles/postgres_upgrade_script # one-time use
];
# Backups
age.secrets.borg-password.file = "${self}/secrets/borg-password.age";
services.postgresqlBackup = {
enable = true;
databases = [ "wallabag" "immich" "ulogger" ];
location = "/var/backup/postgresql";
databases = [
"wallabag"
"immich"
"ulogger"
];
location = "/tank/backup/postgresql";
};
mossnet.backup = {
enable = true;
name = "mossnet";
paths = [
"/var/lib/taskserver" # taskwarrior
"/var/www/shaarli-config" # sharli
"/var/backup/postgresql" # wallabag
"/var/www/shaarli-config" # shaarli
"/tank/backup/postgresql" # postgresql backups
"/var/lib/radicale" # radicale
"/home/anish/usr/drawing" # syncthing
"/data/books" # calibre-web
# "/home/anish/usr/nonfiction" # syncthing
"/tank/syncthing/drawing" # syncthing
"/tank/books" # calibre-web
"/home/anish/usr/finance" # beancount
"/mnt/two/postgres" # sealight postgres backups TODO remove once moved to capsul
"/mnt/two/photos"
"/mnt/two/music"
"/tank/postgres" # postgres data
"/tank/media/photos"
"/tank/media/music"
];
# seafile
};
# opencode-manager ports
networking.firewall = {
allowedTCPPorts = [
5003 # opencode-manager backend
5173 # opencode-manager frontend
5551 # opencode server
5003 # opencode-manager backend
5173 # opencode-manager frontend
5551 # opencode server
];
allowedTCPPortRanges = [{
from = 7000;
to = 9000;
}]; # ports for testing user changes
allowedTCPPortRanges = [
{
from = 7000;
to = 9000;
}
]; # ports for testing user changes
};
environment.systemPackages = with pkgs; [ lm_sensors ];
hardware.fancontrol = {
enable = false;
config = '''';
};
# hardware.fancontrol = {
# enable = false;
# config = '''';
# };
# Secrets
age.secrets.box-wg.file = "${self}/secrets/box-wg.age";
age.secrets.box-wg.owner = "anish";
age.secrets.borg-key.file = "${self}/secrets/borg-key.age";
+238
View File
@@ -0,0 +1,238 @@
# Disko configuration for box NAS
# NVMe boot drive with LUKS + 3x 4TB ZFS RAIDZ1 pool (~8TB usable)
# Unified encryption: LUKS passphrase unlocks root, ZFS uses keyfile inside encrypted root
#
# NOTE: Using RAIDZ1 (3 drives) temporarily due to DOA drive. Can migrate to RAIDZ2
# with 4 drives later by creating a new pool and copying data.
#
# Installation steps:
#
# 1. Generate the ZFS keyfile and LUKS password:
# dd if=/dev/urandom of=/tmp/tank.key bs=32 count=1
# echo -n "your-luks-password" > /tmp/luks-password
#
# 2. Run disko-install:
# sudo nix run 'github:nix-community/disko/latest#disko-install' -- \
# --flake ~/helm#box \
# --disk nvme /dev/disk/by-id/nvme-CT500P310SSD8_2544543B87C2 \
# --disk zfs1 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D954A2J7 \
# --disk zfs2 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D95FVZVL \
# --disk zfs3 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX42D95M807R
#
# 3. Copy the keyfile and update keylocation:
# sudo mkdir -p /mnt/etc/zfs
# sudo cp /tmp/tank.key /mnt/etc/zfs/tank.key
# sudo chmod 000 /mnt/etc/zfs/tank.key
# sudo zfs set keylocation=file:///etc/zfs/tank.key tank
{
disko.devices = {
disk = {
# Boot drive - 500GB NVMe with LUKS encryption
nvme = {
type = "disk";
device = "/dev/disk/by-id/nvme-placeholder"; # Override with --disk nvme /dev/disk/by-id/...
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "cryptroot";
settings = {
allowDiscards = true;
};
# Passphrase will be prompted during boot
passwordFile = "/tmp/luks-password"; # Only used during install, set this before running disko
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [ "noatime" ];
};
};
};
};
};
};
# ZFS pool drives - 3x 4TB in RAIDZ1
zfs1 = {
type = "disk";
device = "/dev/disk/by-id/placeholder-zfs1"; # Override with --disk zfs1 /dev/disk/by-id/...
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "tank";
};
};
};
};
};
zfs2 = {
type = "disk";
device = "/dev/disk/by-id/placeholder-zfs2";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "tank";
};
};
};
};
};
zfs3 = {
type = "disk";
device = "/dev/disk/by-id/placeholder-zfs3";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "tank";
};
};
};
};
};
};
zpool = {
tank = {
type = "zpool";
mode = "raidz1";
options = {
ashift = "12";
cachefile = "none"; # Needed for disko
};
rootFsOptions = {
compression = "lz4";
atime = "off";
xattr = "sa";
acltype = "posixacl";
# ZFS native encryption
# During install: keyfile at /tmp/tank.key
# After install: install-box.sh copies to /etc/zfs/tank.key and updates keylocation
encryption = "aes-256-gcm";
keyformat = "raw";
keylocation = "file:///tmp/tank.key";
"com.sun:auto-snapshot" = "false";
};
# Don't mount the pool root directly
mountpoint = null;
datasets = {
# /nix is on the NVMe ext4 root, not on ZFS
# This simplifies boot dependencies - ZFS is purely for data storage
# Parent dataset for all data - inherits encryption
data = {
type = "zfs_fs";
options.mountpoint = "none";
};
# Media datasets
"data/media" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"data/media/music" = {
type = "zfs_fs";
mountpoint = "/tank/media/music";
options.recordsize = "1M"; # Large files benefit from larger recordsize
};
"data/media/photos" = {
type = "zfs_fs";
mountpoint = "/tank/media/photos";
options.recordsize = "1M";
};
"data/media/movies" = {
type = "zfs_fs";
mountpoint = "/tank/media/movies";
options.recordsize = "1M";
};
"data/media/tv" = {
type = "zfs_fs";
mountpoint = "/tank/media/tv";
options.recordsize = "1M";
};
# Other data
"data/books" = {
type = "zfs_fs";
mountpoint = "/tank/books";
options."com.sun:auto-snapshot" = "true";
};
"data/podcasts" = {
type = "zfs_fs";
mountpoint = "/tank/podcasts";
};
"data/postgres" = {
type = "zfs_fs";
mountpoint = "/tank/postgres";
options = {
recordsize = "16K"; # Better for databases
"com.sun:auto-snapshot" = "true";
};
};
"data/syncthing" = {
type = "zfs_fs";
options.mountpoint = "none";
options."com.sun:auto-snapshot" = "true";
};
"data/syncthing/drawing" = {
type = "zfs_fs";
mountpoint = "/tank/syncthing/drawing";
};
"data/backup" = {
type = "zfs_fs";
mountpoint = "/tank/backup";
options."com.sun:auto-snapshot" = "true";
};
"data/ftp" = {
type = "zfs_fs";
mountpoint = "/tank/ftp";
};
"data/cache" = {
type = "zfs_fs";
mountpoint = "/tank/cache";
options."com.sun:auto-snapshot" = "false";
};
"data/playlists" = {
type = "zfs_fs";
mountpoint = "/tank/playlists";
};
"data/new-music" = {
type = "zfs_fs";
mountpoint = "/tank/new-music";
};
"data/archive" = {
type = "zfs_fs";
mountpoint = "/tank/archive";
options."com.sun:auto-snapshot" = "true";
};
};
};
};
};
}
+24 -56
View File
@@ -1,66 +1,34 @@
# 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, ... }:
# Hardware configuration for box NAS
# Filesystem mounts are handled by disko.nix
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports =
[
(modulesPath + "/installer/scan/not-detected.nix")
];
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "thunderbolt" "uas" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"thunderbolt"
"uas"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{
device = "/dev/disk/by-uuid/ade0752d-84d3-4e39-865b-9027ba2d5c67";
fsType = "ext4";
};
# Filesystems are managed by disko - do not define them here
fileSystems."/boot/efi" =
{
device = "/dev/disk/by-uuid/1715-278E";
fsType = "vfat";
};
fileSystems."/mnt/one" =
{
device = "/dev/disk/by-uuid/0f857c6e-509d-436f-9e78-bc25f1b0d23b";
fsType = "ext4";
options = [
"noatime"
"nodiratime"
"nofail"
];
};
fileSystems."/mnt/two" =
{
device = "/dev/disk/by-uuid/5bc894bf-ed87-4c30-aab4-87e154e0cd08";
fsType = "ext4";
options = [
"noatime"
"nodiratime"
"nofail"
];
};
fileSystems."/mnt/three" =
{
device = "/dev/disk/by-uuid/0be3ded1-9c8b-40aa-94ca-dc2297d5988e";
fsType = "ext4";
options = [
"noatime"
"nodiratime"
"nofail"
];
};
swapDevices =
[{ device = "/dev/disk/by-uuid/b790abb4-ba5f-4476-8f09-b0fc575414aa"; }];
swapDevices = [ ];
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;