How to setup s3fs mount

I am trying to mount an s3fs volume in NixOS (19.09).

I tried a configuration as follows:

  fileSystems."foo" = {
    device = "foo";
    mountPoint = "/foo";
    fsType = "fuse.s3fs";
    noCheck = true;
    options = [ "_netdev" "ro" "allow_other" "use_path_request_style" "url=https://myobjectstore.com" "passwd_file=/root/.passwd-s3fs" "umask=0222" ];
  };

But this gives me the following kind of error:

warning: the following units failed: foo.mount

● foo.mount - /foo
   Loaded: loaded (/etc/fstab; generated)
   Active: failed (Result: exit-code) since Mon 2020-03-16 18:49:27 UTC; 34ms ago
    Where: /foo
     What: foo
     Docs: man:fstab(5)
           man:systemd-fstab-generator(8)
       IP: 0B in, 0B out
      CPU: 4ms

systemd[1]: Mounting /foo...
mount[11343]: /bin/sh: s3fs: command not found
systemd[1]: foo.mount: Mount process exited, code=exited, status=127/n/a
systemd[1]: foo.mount: Failed with result 'exit-code'.
systemd[1]: Failed to mount /foo.

Note that I added s3fs to the default environment using:

  environment.systemPackages = [
    pkgs.s3fs
  ];

and manually calling s3fs to mount the bucket works.

Anyone got any pointers on how to let mount/fuse find the s3fs binary?

1 Like

What worked for me was running the fuse process as a systemd service.

I found an example in nixos/modules/virtualisation/lxcfs.nix, and modified it for S3FS:

{ pkgs, lib, ... }:
let
  s3fs = { mount, bucket }: {
    systemd.services."s3fs-${bucket}" = {
      description = "Linode object storage S3FS";
      wantedBy = [ "multi-user.target" ];
      after = [ "${bucket}-key.service" ];
      requires = [ "${bucket}-key.service" ];
      serviceConfig = {
        ExecStartPre = [
          "${pkgs.coreutils}/bin/mkdir -m 0500 -pv ${mount}"
          "${pkgs.e2fsprogs}/bin/chattr +i ${mount}"  # Stop files being accidentally written to unmounted directory
        ];
        ExecStart = let
          options = [
            "passwd_file=/run/keys/${bucket}"
            "use_path_request_style"
            "allow_other"
            "url=https://ap-south-1.linodeobjects.com/"  # Linode object storage
            "umask=0077"
          ];
        in
          "${pkgs.s3fs}/bin/s3fs ${bucket} ${mount} -f "
            + lib.concatMapStringsSep " " (opt: "-o ${opt}") options;
        ExecStopPost = "-${pkgs.fuse}/bin/fusermount -u ${mount}";
        KillMode = "process";
        Restart = "on-failure";
      };
    };
    deployment.keys."${bucket}".permissions = "0600";
  };
in
  s3fs {
    mount = "/mnt/stuff";
    bucket = "my-stuff";
  }
1 Like

Thanks for the reply. I finally solved this by creating a systemd service as well.

However, I believe that it should be made possible to have it work via the fileSystems service. I am not sure if this is a ‘bug’ in the nixos module or if I am just missing some configuration parameter.

1 Like

I thought I would reply anyway after 10 months with my solution, because I found this thread by google.

As far as I could tell, there is no configuration parameter to set the execution path. You might be able to set the fsType to be an absolute path to the fuse program, not sure though. The fileSystems options translate directly to /etc/fstab, which is picked up by systemd. There is a configuration option systemd.services.<name>.path but no systemd.mounts.<name>.path.

The might be a case for adding the path option to mounts and fileSystems, in order to support fuse filesystems, or alternatively, for a special nixos module to handle fuse filesystems.

1 Like

As I also found this thread from a web search, I wanted to share a solution I found (open-vm-tools: Shared folders not working · Issue #46529 · NixOS/nixpkgs · GitHub):

Important bit:

fsType = "fuse./run/current-system/sw/bin/<fuse exec name>";

Note: the package must be added to environment.systemPackages for this to work

The original example updated:

  fileSystems."foo" = {
    device = "foo";
    mountPoint = "/foo";
    fsType = "fuse./run/current-system/sw/bin/s3fs";
    noCheck = true;
    options = [ "_netdev" "ro" "allow_other" "use_path_request_style" "url=https://myobjectstore.com" "passwd_file=/root/.passwd-s3fs" "umask=0222" ];
  };
2 Likes

Great! I can confirm that this works. The device value needs to be the bucket name, and in our case we wanted to mount a subpath of the bucket which is possible with device = "bucket:/mypath".

I do find it strange that using fuse.${pkgs.s3fs}/bin/s3fs doesn’t work (as also observed in this comment)

1 Like