nix-community / disko

Declarative disk partitioning and formatting using nix [maintainers=@Lassulus @Enzime @iFreilicht]
MIT License
1.78k stars 192 forks source link

Disable fsck for MergerFS? #840

Open dinvlad opened 1 day ago

dinvlad commented 1 day ago

First of all, thanks for the excellent piece of software!

I've configured MergerFS through Disko:

    mergerfs = {
        type = "filesystem";
        device = "/mnt/data_*";
        content = {
          type = "filesystem";
          format = "fuse.mergerfs";
          mountpoint = "/mnt/data";
          mountOptions = [
            "noatime"
            "defaults"
            "cache.files=partial"
            "dropcacheonclose=true"
            "category.create=mfs"
          ];
        };
      };
    };

(here, disk(s) already mounted to /mnt/data_1, /mnt/data_2 are then mounted as a combined FUSE filesystem to /mnt/data)

This works, but at mount time I see the following in dmesg:

[   22.376728] systemd-fstab-generator[1683]: Checking was requested for "/mnt/data_*", but it is not a device.

And fstab shows this:

➜  ~ grep '/mnt/data ' /etc/fstab
/mnt/data_* /mnt/data fuse.mergerfs noatime,defaults,cache.files=partial,dropcacheonclose=true,category.create=mfs 0 2

As you can see, fsck check is enabled due to the last flag (2).

Is it possible to disable it through Disko config somehow? Thank you!

iFreilicht commented 15 hours ago

First of all, a really fun hack that uses the fact that we allow arbitrary strings in the format. AFAIU, you only used disko --mode mount or even just the module, right? Because I think --mode format would fail with this, as /mnt/data_* is not an actual device.

I think the "proper" solution for this issue would be for us to support mergerfs as an actual filesystem, with options dedicated to it, but as a workaround for your situation, it should be enough to add this top-level option to your config:

  fileSystems.mergerfs.noCheck = lib.mkForce true;

Just out of curiosity, did you have to do anything else to get mergerfs to work? Also, could you try if instead of "fuse.mergerfs", simply "mergerfs" works as well?

dinvlad commented 3 hours ago

OK, thanks for your suggestions! I set lib.mkForce true and format = "mergerfs" successfully.

Other than that, the only other pieces I added earlier to make it work, in configuration.nix, are:

  environment.systemPackages = with pkgs; {
    mergerfs
    # ...
  };

  systemd.services.mergerfs-uncache = {
    path = [
      (pkgs.python3.withPackages (ps: with ps; [
        aiofiles
      ]))
    ];
    serviceConfig = {
      Type = "oneshot";
      ExecStart = ''
        ${./mergerfs-uncache.py} -s /mnt/cache -d /mnt/mergerfs_slow -a 90 -t 90
      '';
    };
    startAt = "Sat 00:00:00";
  };

where mergerfs-uncache.py is a slightly modified version of https://github.com/notthebee/nix-config/blob/main/modules/mover/mergerfs-uncache.py (I added -a, --atime option handling). This script is used to move more rarely accessed data from /mnt/cache (which is my fast NVME RAID 1 array) to /mnt/mergerfs_slow (HDD SnapRAID array).

Note also that since my last post, I've modified Disko config to use 2 nested MergerFS filesystems, such that I could access the combined NVME+HDD storage under /data (and under the hood, it would first write the data to NVMEs and then slowly over time move it to HDDs - thus achieving a tiered storage solution):

let
  cacheArray = "/mnt/cache";
  slowArray = "/mnt/data*";
  mergerArrays = {
    mergerfs_slow = {
      device = slowArray;
      mountpoint = "/mnt/mergerfs_slow";
      extraOpts = [];
    };
    data = {
      device = "${cacheArray}:${slowArray}";
      mountpoint = "/data";
      extraOpts = ["category.create=epff"];
    };
  };
  # ...
in
  { disks ? [], lib, ... }: {

  fileSystems = lib.mapAttrs' (_: fs: lib.nameValuePair fs.mountpoint {
    noCheck = lib.mkForce true;
  }) mergerArrays;

  disko.devices = {
    disk = (
      lib.mapAttrs (name: fs : {
        type = "filesystem";
        device = fs.device;
        content = {
          type = "filesystem";
          format = "fuse.mergerfs";
          mountpoint = fs.mountpoint;
          mountOptions = [
            "defaults"
            "moveonenospc=1"
            "minfreespace=100G"
            "func.getattr=newest"
            "cache.files=partial"
            "dropcacheonclose=true"
            "fsname=${name}"
          ] ++ fs.extraOpts;
        };
      }) mergerArrays
    ) // # ... the rest of Disko config, for the actual disks
};