storage-refactor #9

Open
jan-leila wants to merge 53 commits from storage-refactor into main
7 changed files with 185 additions and 42 deletions
Showing only changes of commit 65e0c6e0e5 - Show all commits

View file

@ -67,6 +67,7 @@
}; };
storage = { storage = {
generateBase = false;
zfs = { zfs = {
enable = true; enable = true;
notifications = { notifications = {

View file

@ -4,6 +4,7 @@
./hardware-configuration.nix ./hardware-configuration.nix
./configuration.nix ./configuration.nix
./packages.nix ./packages.nix
./legacy-storage.nix
./legacy-impermanence.nix ./legacy-impermanence.nix
]; ];
} }

View file

@ -14,7 +14,17 @@
... ...
}: { }: {
config = lib.mkIf config.storage.impermanence.enable { config = lib.mkIf config.storage.impermanence.enable {
environment.persistence."/persist/replicate/system/root" = { system.activationScripts = {
"var-lib-private-permissions" = {
deps = ["specialfs"];
text = ''
mkdir -p /persist/system/root/var/lib/private
chmod 0700 /persist/system/root/var/lib/private
'';
};
};
environment.persistence."/persist/system/root" = {
enable = true; enable = true;
hideMounts = true; hideMounts = true;
directories = lib.mkMerge [ directories = lib.mkMerge [
@ -78,7 +88,7 @@
} }
]) ])
# Jellyfin # Jellyfin (data/cache only - media is on separate dataset)
(lib.mkIf config.services.jellyfin.enable [ (lib.mkIf config.services.jellyfin.enable [
{ {
directory = "/var/lib/jellyfin"; directory = "/var/lib/jellyfin";
@ -90,12 +100,6 @@
user = "jellyfin"; user = "jellyfin";
group = "jellyfin"; group = "jellyfin";
} }
{
directory = config.services.jellyfin.media_directory;
user = "jellyfin";
group = "jellyfin_media";
mode = "1770";
}
]) ])
# Immich # Immich
@ -152,19 +156,13 @@
} }
]) ])
# qBittorrent # qBittorrent (config only - media is on separate dataset)
(lib.mkIf config.services.qbittorrent.enable [ (lib.mkIf config.services.qbittorrent.enable [
{ {
directory = "/var/lib/qBittorrent/"; directory = "/var/lib/qBittorrent/";
user = "qbittorrent"; user = "qbittorrent";
group = "qbittorrent"; group = "qbittorrent";
} }
{
directory = config.services.qbittorrent.mediaDir;
user = "qbittorrent";
group = "qbittorrent";
mode = "1775";
}
]) ])
# Sonarr # Sonarr
@ -222,5 +220,42 @@
]) ])
]; ];
}; };
# Jellyfin media on separate dataset (matching main)
environment.persistence."/persist/system/jellyfin" = lib.mkIf config.services.jellyfin.enable {
enable = true;
hideMounts = true;
directories = [
{
directory = config.services.jellyfin.media_directory;
user = "jellyfin";
group = "jellyfin_media";
mode = "1770";
}
];
};
# qBittorrent media on separate dataset (matching main)
environment.persistence."/persist/system/qbittorrent" = lib.mkIf config.services.qbittorrent.enable {
enable = true;
hideMounts = true;
directories = [
{
directory = config.services.qbittorrent.mediaDir;
user = "qbittorrent";
group = "qbittorrent";
mode = "1775";
}
];
};
# /var/log persistence (matching main)
environment.persistence."/persist/system/var/log" = {
enable = true;
hideMounts = true;
directories = [
"/var/log"
];
};
}; };
} }

View file

@ -0,0 +1,103 @@
# Legacy storage configuration for defiant
# This file manually defines ZFS datasets matching the main branch structure
# to allow incremental migration to the new storage module.
#
# Datasets from main branch:
# - local/ - ephemeral parent
# - local/home/leyla - ephemeral user home
# - local/system/nix - nix store
# - local/system/root - root filesystem (rolled back on boot)
# - local/system/sops - sops age key
# - persist/ - persistent parent
# - persist/home/leyla - persistent user home
# - persist/system/jellyfin - jellyfin media
# - persist/system/qbittorrent - qbittorrent media
# - persist/system/root - persistent root data
# - persist/system/var/log - log persistence
{lib, ...}: {
# Manually define ZFS datasets matching main's structure
storage.zfs.datasets = {
# Ephemeral datasets (local/)
"local" = {
type = "zfs_fs";
mount = null;
};
"local/home/leyla" = {
type = "zfs_fs";
mount = "/home/leyla";
snapshot = {
blankSnapshot = true;
};
};
"local/system/nix" = {
type = "zfs_fs";
mount = "/nix";
atime = "off";
relatime = "off";
snapshot = {
autoSnapshot = false;
};
};
"local/system/root" = {
type = "zfs_fs";
mount = "/";
snapshot = {
blankSnapshot = true;
};
};
"local/system/sops" = {
type = "zfs_fs";
mount = "/persist/sops";
};
# Persistent datasets (persist/)
"persist" = {
type = "zfs_fs";
mount = null;
};
"persist/home/leyla" = {
type = "zfs_fs";
mount = "/persist/home/leyla";
snapshot = {
autoSnapshot = true;
};
};
"persist/system/jellyfin" = {
type = "zfs_fs";
mount = "/persist/system/jellyfin";
atime = "off";
relatime = "off";
};
"persist/system/qbittorrent" = {
type = "zfs_fs";
mount = "/persist/system/qbittorrent";
atime = "off";
relatime = "off";
};
"persist/system/root" = {
type = "zfs_fs";
mount = "/persist/system/root";
snapshot = {
autoSnapshot = true;
};
};
"persist/system/var/log" = {
type = "zfs_fs";
mount = "/persist/system/var/log";
};
};
# Boot commands to rollback ephemeral root on boot
boot.initrd.postResumeCommands = lib.mkAfter ''
zfs rollback -r rpool/local/system/root@blank
'';
# FileSystems needed for boot
fileSystems = {
"/".neededForBoot = true;
"/persist/system/root".neededForBoot = true;
"/persist/system/var/log".neededForBoot = true;
"/persist/system/jellyfin".neededForBoot = true;
"/persist/system/qbittorrent".neededForBoot = true;
};
}

14
flake.lock generated
View file

@ -129,20 +129,12 @@
} }
}, },
"impermanence": { "impermanence": {
"inputs": {
"home-manager": [
"home-manager"
],
"nixpkgs": [
"nixpkgs"
]
},
"locked": { "locked": {
"lastModified": 1767822991, "lastModified": 1737831083,
"narHash": "sha256-iyrn9AcPZCoyxX4OT8eMkBsjG7SRUQXXS/V1JzxS7rA=", "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
"owner": "nix-community", "owner": "nix-community",
"repo": "impermanence", "repo": "impermanence",
"rev": "82e5bc4508cab9e8d5a136626276eb5bbce5e9c5", "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -66,10 +66,11 @@ in {
} }
]; ];
# fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254
system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"];
system.activationScripts = { system.activationScripts = {
"var-lib-private-permissions" = { # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254
"createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"];
"var-lib-private-permissions" = lib.mkIf config.storage.generateBase {
deps = ["specialfs"]; deps = ["specialfs"];
text = '' text = ''
mkdir -p /persist/replicate/system/root/var/lib/private mkdir -p /persist/replicate/system/root/var/lib/private

View file

@ -22,7 +22,16 @@ args @ {
# Find options that are only in impermanence datasets (not in regular ZFS datasets) # Find options that are only in impermanence datasets (not in regular ZFS datasets)
impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions;
in { in {
options.storage.datasets = { options.storage = {
generateBase = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
When enabled, enables automatic generation of base datasets (ephemeral, local, replicate roots).
This allows manual definition of datasets matching an existing system layout for migration purposes.
'';
};
datasets = {
ephemeral = lib.mkOption { ephemeral = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); type = lib.types.attrsOf (lib.types.submodule datasetSubmodule);
default = {}; default = {};
@ -36,9 +45,10 @@ in {
default = {}; default = {};
}; };
}; };
};
config = lib.mkMerge [ config = lib.mkMerge [
(lib.mkIf config.storage.zfs.enable { (lib.mkIf (config.storage.zfs.enable && config.storage.generateBase) {
# Create ZFS datasets based on storage.datasets configuration # Create ZFS datasets based on storage.datasets configuration
storage.datasets = { storage.datasets = {
local = { local = {
@ -55,7 +65,7 @@ in {
}; };
}; };
}) })
(lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable) { (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable && config.storage.generateBase) {
storage.datasets = { storage.datasets = {
ephemeral = { ephemeral = {
"" = { "" = {