diff --git a/hosts/testienix/default.nix b/hosts/testienix/default.nix index 264a7ee..0411a83 100644 --- a/hosts/testienix/default.nix +++ b/hosts/testienix/default.nix @@ -11,6 +11,7 @@ ../../modules/hardware/zfs.nix ../../modules/services/samba.nix + ../../modules/services/nextcloud.nix ./net.nix ./fs.nix diff --git a/lib/containers.nix b/lib/containers.nix new file mode 100644 index 0000000..4108b07 --- /dev/null +++ b/lib/containers.nix @@ -0,0 +1,24 @@ +_inputs: _self: super: { + lib = + super.lib + // { + containers.mkConfig = name: config: + super.lib.mkMerge [ + { + bindmounts = { + "state" = { + mountpoint = "/state"; + hostPath = "/state/containers/${name}"; + }; + "persist" = { + mountpoint = "/persist"; + hostPath = config.zfs.mountpoint; + }; + }; + #config = {...}: { + #}; + } + config + ]; + }; +} diff --git a/lib/default.nix b/lib/default.nix index 4665e40..49043ee 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,3 +1,4 @@ inputs: [ (import ./disko.nix inputs) + (import ./containers.nix inputs) ] diff --git a/lib/disko.nix b/lib/disko.nix index 6d2fce5..a513903 100644 --- a/lib/disko.nix +++ b/lib/disko.nix @@ -61,6 +61,7 @@ _inputs: _self: super: { "local/nix" = filesystem "/nix"; "local/state" = filesystem "/state"; "safe" = unmountable; + "safe/containers" = unmountable; "safe/persist" = filesystem "/persist"; }; unmountable = {type = "zfs_fs";}; diff --git a/modules/services/containers.nix b/modules/services/containers.nix new file mode 100644 index 0000000..7a24c12 --- /dev/null +++ b/modules/services/containers.nix @@ -0,0 +1,107 @@ +{ + config, + lib, + utils, + pkgs, + ... +}: let + inherit + (lib) + mapAttrsToList + flip + types + mkOption + mkEnableOption + mdDoc + mkIf + disko + makeBinPath + escapeShellArg + mkMerge + ; +in { + options.containers = mkOption { + type = types.attrsOf (types.submodule ( + {name, ...}: { + options = { + zfs = { + enable = mkEnableOption (mdDoc "persistent data on separate zfs dataset"); + + pool = mkOption { + type = types.str; + description = mdDoc "The host's zfs pool on which the dataset resides"; + }; + + dataset = mkOption { + type = types.str; + default = "safe/containers/${name}"; + description = mdDoc "The host's dataset that should be used for this containers persistent data (will automatically be created, parent dataset must exist)"; + }; + + mountpoint = mkOption { + type = types.str; + default = "/containers/${name}"; + description = mdDoc "The host's mountpoint for the containers dataset"; + }; + }; + }; + } + )); + }; + config.disko = mkMerge (flip mapAttrsToList config.containers + ( + _: cfg: { + devices.zpool = mkIf cfg.zfs.enable { + ${cfg.zfs.pool}.datasets."${cfg.zfs.dataset}" = + disko.zfs.filesystem cfg.zfs.mountpoint; + }; + + # Ensure that the zfs dataset exists before it is mounted. + } + )); + config.systemd = mkMerge (flip mapAttrsToList config.containers + ( + name: cfg: { + services = let + fsMountUnit = "${utils.escapeSystemdPath cfg.zfs.mountpoint}.mount"; + in + mkIf cfg.zfs.enable { + # Ensure that the zfs dataset exists before it is mounted. + "zfs-ensure-${utils.escapeSystemdPath cfg.zfs.mountpoint}" = { + wantedBy = [fsMountUnit]; + before = [fsMountUnit]; + after = [ + "zfs-import-${utils.escapeSystemdPath cfg.zfs.pool}.service" + "zfs-mount.target" + ]; + unitConfig.DefaultDependencies = "no"; + serviceConfig.Type = "oneshot"; + script = let + poolDataset = "${cfg.zfs.pool}/${cfg.zfs.dataset}"; + diskoDataset = config.disko.devices.zpool.${cfg.zfs.pool}.datasets.${cfg.zfs.dataset}; + in '' + export PATH=${makeBinPath [pkgs.zfs]}":$PATH" + if ! zfs list -H -o type ${escapeShellArg poolDataset} &>/dev/null ; then + ${diskoDataset._create} + fi + ''; + }; + + # Ensure that the zfs dataset has the correct permissions when mounted + "zfs-chown-${utils.escapeSystemdPath cfg.zfs.mountpoint}" = { + after = [fsMountUnit]; + unitConfig.DefaultDependencies = "no"; + serviceConfig.Type = "oneshot"; + script = '' + chmod 700 ${escapeShellArg cfg.zfs.mountpoint} + ''; + }; + + "container@${name}" = { + requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath cfg.zfs.mountpoint}.service"]; + after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath cfg.zfs.mountpoint}.service"]; + }; + }; + } + )); +} diff --git a/modules/services/nextcloud.nix b/modules/services/nextcloud.nix index 79a80c2..625ac69 100644 --- a/modules/services/nextcloud.nix +++ b/modules/services/nextcloud.nix @@ -1,6 +1,15 @@ -{lib, ...}: { - containers.nextcloud = { +{ + lib, + stateVersion, + ... +}: { + imports = [./containers.nix]; + containers.nextcloud = lib.container.mkConfig "nextcloud" { autoStart = true; + zfs = { + enable = true; + pool = "panzer"; + }; macvlans = [ "lan01" ]; @@ -16,7 +25,7 @@ config.adminpassFile = "${pkgs.writeText "adminpass" "test123"}"; # DON'T DO THIS IN PRODUCTION - the password file will be world-readable in the Nix Store! }; - system.stateVersion = "23.05"; + system.stateVersion = stateVersion; networking = { firewall = { @@ -28,10 +37,19 @@ }; services.resolved.enable = true; - bindMounts.data = { - mountPoint = "/persist"; - hostPath = "/persist/containers/nextcloud"; - }; }; }; } +#wireguard +#samba/printer finding +#vaultwarden +#nextcloud +#acme +#nginx +#maddy +#kanidm +#xdg portals +#zfs snapshots +#remote backups +#immich +