Jump to content

NFS: Difference between revisions

From Official NixOS Wiki
imported>Patryk27
No edit summary
DHCP (talk | contribs)
m Client: add missing $
 
(22 intermediate revisions by 13 users not shown)
Line 1: Line 1:
__FORCETOC__
__FORCETOC__
== Server ==


Let's say that we've got one server-machine with 4 directories that we want to share: <code>/mnt/kotomi</code>, <code>/mnt/mafuyu</code>, <code>/mnt/sen</code> and <code>/mnt/tomoyo</code>.
[[wikipedia:Network_File_System|NFS]] is a distribute filesystem protocol to access directories and files over a network.  


First, we have to create a dedicated directory from which our NFS server will access the data:
= Server =
 
== NFS share setup ==
 
=== Using bind mounts ===
 
When deploying NFS, it is considered best practice to bind mount directories to be shared to a "virtual root directory" (typically <code>/export</code>) as filesystems to be exported can be made available under a single directory.
 
{{Note|This section demonstrates a best-practice NFS deployment using a virtual root directory (as in [https://wiki.gentoo.org/wiki/NFSv4 nfs-utils - Gentoo wiki]). Instead the directory can be directly exported as follows:
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
  services.nfs.server.enable = true;
  services.nfs.server.exports = ''
    /home    192.0.2.0/24(insecure,rw,sync,no_subtree_check)
  '';
}
</nowiki>}}
}}
 
Let's say that we've got one server-machine with 2 directories that we want to share: <code>/mnt/tomoyo</code> and <code>/mnt/kotomi</code>.
 
First, we have to create a dedicated directory ("virtual root directory") from which our NFS server will access the data:


<syntaxhighlight lang="console">
<syntaxhighlight lang="console">
Line 10: Line 31:
</syntaxhighlight>
</syntaxhighlight>


Then we have to either move our already-existing directories inside <code>/export</code> or bind-mount them there:
You may need to change ownership of the <code>/export</code> directory to <code>nobody:nogroup</code>.


<syntaxhighlight lang="nix">
Next mount directories to the virtual root directory.
{
 
  fileSystems."/export/mafuyu" = {
<syntaxhighlight lang="console">
    device = "/mnt/mafuyu";
# mount --bind /mnt/tomoyo /export/tomoyo
    options = [ "bind" ];
# mount --bind /mnt/kotomi /export/kotomi
  };
</syntaxhighlight>
 
Generate hardware config:
 
<syntaxhighlight lang="console">
nixos-generate-config
</syntaxhighlight>


  fileSystems."/export/sen" = {
This will add the following to <code>hardware-configuration.nix</code>.
    device = "/mnt/sen";
    options = [ "bind" ];
  };


{{file|/etc/nixos/hardware-configuration.nix|nix|<nowiki>
{
   fileSystems."/export/tomoyo" = {
   fileSystems."/export/tomoyo" = {
     device = "/mnt/tomoyo";
     device = "/mnt/tomoyo";
    fsType = "none";
     options = [ "bind" ];
     options = [ "bind" ];
   };
   };
Line 31: Line 58:
   fileSystems."/export/kotomi" = {
   fileSystems."/export/kotomi" = {
     device = "/mnt/kotomi";
     device = "/mnt/kotomi";
    fsType = "none";
     options = [ "bind" ];
     options = [ "bind" ];
   };
   };
}
}
</syntaxhighlight>
</nowiki>}}
 
Refer to [[Filesystems#Bind mounts]] for more information on bind mounts.
 
=== Using btrfs subvolumes ===
 
If you are using Btrfs, instead of moving existing directories or bind-mounting them into <code>/export</code>, you can create dedicated subvolumes directly under <code>/export</code>. This avoids the need for additional bind mounts and makes snapshotting or quota management easier. See [[btrfs#Subvolumes]] for details on creating subvolumes.
 
== NFS service configuration ==


Having the filesystem ready, we can proceed to configure our NFS server:
Having the filesystem ready, we can proceed to configure the NFS server itself:


<syntaxhighlight lang="nix">{
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
   services.nfs.server.enable = true;
   services.nfs.server.enable = true;
   services.nfs.server.exports = ''
   services.nfs.server.exports = ''
     /export        192.168.1.10(rw,fsid=0,no_subtree_check) 192.168.1.15(rw,fsid=0,no_subtree_check)
     /export        192.168.1.10(rw,fsid=0,no_subtree_check) 192.168.1.15(rw,fsid=0,no_subtree_check)
     /export/kotomi  192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
     /export/kotomi  192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
    /export/mafuyu  192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
    /export/sen    192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
     /export/tomoyo  192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
     /export/tomoyo  192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
   '';
   '';
}</syntaxhighlight>
}
This configuration exports all our shares to 2 local IPs; Gentoo's wiki has a great article [https://wiki.gentoo.org/wiki/NFSv4#Server Gentoo wiki NFSv4 article] that covers typical settings, so we won't repeat them here.
</nowiki>}}
 
This configuration exposes all our shares to 2 local IPs; you can find more examples at [https://wiki.gentoo.org/wiki/NFSv4 Gentoo's wiki on NFS].
 
To list the current loaded exports, use: <code>exportfs -v</code>


Other options are available on the [https://search.nixos.org/options?query=nfs NixOS option page] or via the <code>nixos-option</code> command.
Other options are available on the [https://search.nixos.org/options?query=nfs NixOS option page] or via the <code>nixos-option</code> command.
{{note| If you are exporting a btrfs subvolume, it is recommended to use the fsid option with a unique id, e.g. <code>fsid{{=}}12345</code>. See the [https://btrfs.readthedocs.io/en/latest/Interoperability.html#nfs btrfs interoperability docs] for more info.}}
=== Firewall ===


If your server-machine has a firewall turned on (as NixOS does by default, for instance), don't forget to open appropriate ports; e.g. for NFSv4:
If your server-machine has a firewall turned on (as NixOS does by default, for instance), don't forget to open appropriate ports; e.g. for NFSv4:
Line 57: Line 100:
</syntaxhighlight>
</syntaxhighlight>


== Client ==
Many clients only support NFSv3, which requires the server to have fixed ports:
<syntaxhighlight lang="nix">
  services.nfs.server = {
    enable = true;
    # fixed rpc.statd port; for firewall
    lockdPort = 4001;
    mountdPort = 4002;
    statdPort = 4000;
    extraNfsdConfig = '''';
  };
  networking.firewall = {
    enable = true;
      # for NFSv3; view with `rpcinfo -p`
    allowedTCPPorts = [ 111  2049 4000 4001 4002 20048 ];
    allowedUDPPorts = [ 111 2049 4000 4001  4002 20048 ];
  };
</syntaxhighlight>


Continuing the server example, mounting the now-exposed ''tomoyo'' share on another box (on a client) is as simple as:
= Client =


To ensure the client has the necessary NFS utilities installed to mount NFS drives, add "nfs" to {{nixos:option|boot.supportedFilesystems}}.
{{file|/etc/nixos/hardware-configuration.nix|nix|<nowiki>
boot.supportedFilesystems = [ "nfs" ];
</nowiki>}}
{{warning|Without addding "nfs" to {{nixos:option|boot.supportedFilesystems}}, the following error will be raised when trying to mount them:
<syntaxhighlight lang="console">
# mount 192.0.2.1:/tomoyo /mnt/tomoyo
mount: /mnt/nfs: fsconfig system call failed: NFS: mount program didn't pass remote address.
      dmesg(1) may have more information after failed mount system call.
</syntaxhighlight>
}}
Next mount exports to your local directories:
<syntaxhighlight lang="console">
# mount 192.0.2.1:/tomoyo /mnt/tomoyo
# mount 192.0.2.1:/kotomi /mnt/kotomi
</syntaxhighlight>
{{Note|Replace "192.0.2.1" with the appropriate IP address or DNS entry of your NFS server.}}
{{note| On the client side, the exposed shares are as if they were exposed at the root level - i.e. <code>/export/foo</code> becomes <code>/foo</code> (in the <code>device</code> option) }}
Generate hardware config:
<syntaxhighlight lang="console">
$ nixos-generate-config
</syntaxhighlight>
This will add the following to <code>hardware-configuration.nix</code>.
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
  fileSystems."/mnt/tomoyo" = {
    device = "192.0.2.1:/tomoyo";
    fsType = "nfs4";
  };
  fileSystems."/mnt/kotomi" = {
    device = "192.0.2.1:/kotomi";
    fsType = "nfs4";
  };
}
</nowiki>}}
Other, regular [https://search.nixos.org/options?query=filesystems.%3Cname%3E filesystem options] apply.
== Specifying NFS version ==
You can specify NFS version by adding the <code>"nfsvers="</code> option:
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
   fileSystems."/mnt/tomoyo" = {
   fileSystems."/mnt/tomoyo" = {
     device = "server:/tomoyo";
     # ...
     fsType = "nfs";
     options = [ "nfsvers=4.2" ];
   };
   };
}
}
</syntaxhighlight>
</syntaxhighlight>
Note that clients see exposed shares as if they were exposed at the root level - i.e. ''/export/foo'' becomes ''/foo'' (in the <code>device</code> option). Other, regular '''fileSystems''' options apply.


Tip: you can specify NFS version by adding the <code>"nfsvers="</code> option:
== Lazy-mounting ==
 
By default, all shares will be mounted right when your machine starts - apart from being simply unwanted sometimes, this may also cause issues when your computer doesn't have a stable network connection or uses WiFi; you can fix this by telling systemd to mount your shares the first time they are ''accessed'' (instead of keeping them mounted at all times):
 
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
   fileSystems."/mnt/tomoyo" = {
   fileSystems."/mnt/tomoyo" = {
     /* ... */
     # ...
     options = [ "nfsvers=4.2" ];
     options = [ "x-systemd.automount" "noauto" ];
   };
   };
}
}
</syntaxhighlight>
</syntaxhighlight>


Tip: by default, all shares will be mounted right when your machine starts - apart from being simply unwanted sometimes, this may also cause issues when your computer doesn't have a stable network connection or uses WiFi; you can fix this by telling systemd to mount your shares the first time they are ''accessed'' (instead of keeping them mounted at all times):
== Auto-disconnecting ==
 
You can tell systemd to disconnect your NFS-client from the NFS-server when the directory has not been accessed for some time:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
   fileSystems."/mnt/tomoyo" = {
   fileSystems."/mnt/tomoyo" = {
     /* ... */
     # ...
     options = ["x-systemd.automount" "noauto"];
     options = [ "x-systemd.idle-timeout=600" ]; # disconnects after 10 minutes (i.e. 600 seconds)
   };
   };
}
}
</syntaxhighlight>
</syntaxhighlight>


Tip: you can also tell systemd to disconnect your NFS-client from the NFS-server when the directory has not been accessed for some time:
== Using systemd.mounts and systemd.automounts ==
 
This section provides an alternative approach for users who prefer to manage mounts using dedicated systemd units. Here is an example with auto-disconnecting and lazy-mounting implemented, and the <code>noatime</code> mount option added.
 
Note that <code>wantedBy = [ "multi-user.target" ];</code> is required for the automount unit to start at boot.
 
Also note that <code>x-systemd</code> mount options are unneeded, as they are a representation of systemd options in <code>fstab(5)</code> format. They get parsed and converted to unit files by <code>systemd-fstab-generator(8)</code> as mentioned in <code>systemd.mount(5)</code>.
 
<syntaxhighlight lang="nix">
{
  services.rpcbind.enable = true; # needed for NFS
  systemd.mounts = [{
    type = "nfs";
    mountConfig = {
      Options = "noatime";
    };
    what = "server:/tomoyo";
    where = "/mnt/tomoyo";
  }];
 
  systemd.automounts = [{
    wantedBy = [ "multi-user.target" ];
    automountConfig = {
      TimeoutIdleSec = "600";
    };
    where = "/mnt/tomoyo";
  }];
}
</syntaxhighlight>
 
Multiple mounts with the exact same options can benefit from abstraction.


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
   fileSystems."/mnt/tomoyo" = {
   services.rpcbind.enable = true; # needed for NFS
     /* ... */
  systemd.mounts = let commonMountOptions = {
     options = ["x-systemd.idle-timeout=600"]; # disconnects after 10 minutes (i.e. 600 seconds)
    type = "nfs";
    mountConfig = {
      Options = "noatime";
    };
  };
 
  in
 
  [
    (commonMountOptions // {
      what = "server:/tomoyo";
      where = "/mnt/tomoyo";
    })
 
    (commonMountOptions // {
      what = "server:/kotomi";
      where = "/mnt/kotomi";
     })
  ];
 
  systemd.automounts = let commonAutoMountOptions = {
     wantedBy = [ "multi-user.target" ];
    automountConfig = {
      TimeoutIdleSec = "600";
    };
   };
   };
  in
  [
    (commonAutoMountOptions // { where = "/mnt/tomoyo"; })
    (commonAutoMountOptions // { where = "/mnt/kotomi"; })
  ];
}
}
</syntaxhighlight>
</syntaxhighlight>


== Nix store on NFS ==
= Nix store on NFS =


In a single-user setup ('''not on Nixos''') the Nix store can be also exported over NFS (common in HPC clusters) to share package over the networks. The only requirement is to also pass <code>local_lock=flock</code> or <code>local_lock=all</code> as mount option to allow the nix packages to take locks on modifications. Example entry in <code>fstab</code>:
In a single-user setup ('''not on Nixos''') the Nix store can be also exported over NFS (common in HPC clusters) to share package over the networks. The only requirement is to also pass <code>local_lock=flock</code> or <code>local_lock=all</code> as mount option to allow the nix packages to take locks on modifications. Example entry in <code>fstab</code>:


<syntaxhighlight lang="console"><host_or_ip>/nix /nix nfs nofail,x-systemd.device-timeout=4,local_lock=all 0 0</syntaxhighlight>
<syntaxhighlight lang="console"><host_or_ip>/nix /nix nfs nofail,x-systemd.device-timeout=4,local_lock=all 0 0</syntaxhighlight>'''TODO:''' Why this? That seems extremely unsafe. This disables NFS locks (which apply to all NFS clients), and makes locks ''local'', meaning a lock taken by one NFS client isn't seen by another, and both can take their locks. So this removes protection against concurrent writes, which Nix assumes.
[[Category:Filesystem]]
[[Category:Networking]]

Latest revision as of 15:17, 19 July 2026


NFS is a distribute filesystem protocol to access directories and files over a network.

Server

NFS share setup

Using bind mounts

When deploying NFS, it is considered best practice to bind mount directories to be shared to a "virtual root directory" (typically /export) as filesystems to be exported can be made available under a single directory.

Note: This section demonstrates a best-practice NFS deployment using a virtual root directory (as in nfs-utils - Gentoo wiki). Instead the directory can be directly exported as follows:
❄︎ /etc/nixos/configuration.nix
{
  services.nfs.server.enable = true;
  services.nfs.server.exports = ''
    /home    192.0.2.0/24(insecure,rw,sync,no_subtree_check)
  '';
}

Let's say that we've got one server-machine with 2 directories that we want to share: /mnt/tomoyo and /mnt/kotomi.

First, we have to create a dedicated directory ("virtual root directory") from which our NFS server will access the data:

$ mkdir /export

You may need to change ownership of the /export directory to nobody:nogroup.

Next mount directories to the virtual root directory.

# mount --bind /mnt/tomoyo /export/tomoyo
# mount --bind /mnt/kotomi /export/kotomi

Generate hardware config:

nixos-generate-config

This will add the following to hardware-configuration.nix.

❄︎ /etc/nixos/hardware-configuration.nix
{
  fileSystems."/export/tomoyo" = {
    device = "/mnt/tomoyo";
    fsType = "none";
    options = [ "bind" ];
  };

  fileSystems."/export/kotomi" = {
    device = "/mnt/kotomi";
    fsType = "none";
    options = [ "bind" ];
  };
}

Refer to Filesystems#Bind mounts for more information on bind mounts.

Using btrfs subvolumes

If you are using Btrfs, instead of moving existing directories or bind-mounting them into /export, you can create dedicated subvolumes directly under /export. This avoids the need for additional bind mounts and makes snapshotting or quota management easier. See btrfs#Subvolumes for details on creating subvolumes.

NFS service configuration

Having the filesystem ready, we can proceed to configure the NFS server itself:

❄︎ /etc/nixos/configuration.nix
{
  services.nfs.server.enable = true;
  services.nfs.server.exports = ''
    /export         192.168.1.10(rw,fsid=0,no_subtree_check) 192.168.1.15(rw,fsid=0,no_subtree_check)
    /export/kotomi  192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
    /export/tomoyo  192.168.1.10(rw,nohide,insecure,no_subtree_check) 192.168.1.15(rw,nohide,insecure,no_subtree_check)
  '';
}

This configuration exposes all our shares to 2 local IPs; you can find more examples at Gentoo's wiki on NFS.

To list the current loaded exports, use: exportfs -v

Other options are available on the NixOS option page or via the nixos-option command.

Note: If you are exporting a btrfs subvolume, it is recommended to use the fsid option with a unique id, e.g. fsid=12345. See the btrfs interoperability docs for more info.

Firewall

If your server-machine has a firewall turned on (as NixOS does by default, for instance), don't forget to open appropriate ports; e.g. for NFSv4:

networking.firewall.allowedTCPPorts = [ 2049 ];

Many clients only support NFSv3, which requires the server to have fixed ports:

  services.nfs.server = {
    enable = true;
    # fixed rpc.statd port; for firewall
    lockdPort = 4001;
    mountdPort = 4002;
    statdPort = 4000;
    extraNfsdConfig = '''';
  };
  networking.firewall = {
    enable = true;
      # for NFSv3; view with `rpcinfo -p`
    allowedTCPPorts = [ 111  2049 4000 4001 4002 20048 ];
    allowedUDPPorts = [ 111 2049 4000 4001  4002 20048 ];
  };

Client

To ensure the client has the necessary NFS utilities installed to mount NFS drives, add "nfs" to boot.supportedFilesystems.

❄︎ /etc/nixos/hardware-configuration.nix
boot.supportedFilesystems = [ "nfs" ];
⚠︎
Warning: Without addding "nfs" to boot.supportedFilesystems, the following error will be raised when trying to mount them:
# mount 192.0.2.1:/tomoyo /mnt/tomoyo
mount: /mnt/nfs: fsconfig system call failed: NFS: mount program didn't pass remote address.
       dmesg(1) may have more information after failed mount system call.

Next mount exports to your local directories:

# mount 192.0.2.1:/tomoyo /mnt/tomoyo
# mount 192.0.2.1:/kotomi /mnt/kotomi
Note: Replace "192.0.2.1" with the appropriate IP address or DNS entry of your NFS server.
Note: On the client side, the exposed shares are as if they were exposed at the root level - i.e. /export/foo becomes /foo (in the device option)

Generate hardware config:

$ nixos-generate-config

This will add the following to hardware-configuration.nix.

❄︎ /etc/nixos/configuration.nix
{
  fileSystems."/mnt/tomoyo" = {
    device = "192.0.2.1:/tomoyo";
    fsType = "nfs4";
  };

  fileSystems."/mnt/kotomi" = {
    device = "192.0.2.1:/kotomi";
    fsType = "nfs4";
  };
}

Other, regular filesystem options apply.

Specifying NFS version

You can specify NFS version by adding the "nfsvers=" option:

{
  fileSystems."/mnt/tomoyo" = {
    # ...
    options = [ "nfsvers=4.2" ];
  };
}

Lazy-mounting

By default, all shares will be mounted right when your machine starts - apart from being simply unwanted sometimes, this may also cause issues when your computer doesn't have a stable network connection or uses WiFi; you can fix this by telling systemd to mount your shares the first time they are accessed (instead of keeping them mounted at all times):

{
  fileSystems."/mnt/tomoyo" = {
    # ...
    options = [ "x-systemd.automount" "noauto" ];
  };
}

Auto-disconnecting

You can tell systemd to disconnect your NFS-client from the NFS-server when the directory has not been accessed for some time:

{
  fileSystems."/mnt/tomoyo" = {
    # ...
    options = [ "x-systemd.idle-timeout=600" ]; # disconnects after 10 minutes (i.e. 600 seconds)
  };
}

Using systemd.mounts and systemd.automounts

This section provides an alternative approach for users who prefer to manage mounts using dedicated systemd units. Here is an example with auto-disconnecting and lazy-mounting implemented, and the noatime mount option added.

Note that wantedBy = [ "multi-user.target" ]; is required for the automount unit to start at boot.

Also note that x-systemd mount options are unneeded, as they are a representation of systemd options in fstab(5) format. They get parsed and converted to unit files by systemd-fstab-generator(8) as mentioned in systemd.mount(5).

{
  services.rpcbind.enable = true; # needed for NFS
  systemd.mounts = [{
    type = "nfs";
    mountConfig = {
      Options = "noatime";
    };
    what = "server:/tomoyo";
    where = "/mnt/tomoyo";
  }];

  systemd.automounts = [{
    wantedBy = [ "multi-user.target" ];
    automountConfig = {
      TimeoutIdleSec = "600";
    };
    where = "/mnt/tomoyo";
  }];
}

Multiple mounts with the exact same options can benefit from abstraction.

{
  services.rpcbind.enable = true; # needed for NFS
  systemd.mounts = let commonMountOptions = {
    type = "nfs";
    mountConfig = {
      Options = "noatime";
    };
  };

  in

  [
    (commonMountOptions // {
      what = "server:/tomoyo";
      where = "/mnt/tomoyo";
    })

    (commonMountOptions // {
      what = "server:/kotomi";
      where = "/mnt/kotomi";
    })
  ];

  systemd.automounts = let commonAutoMountOptions = {
    wantedBy = [ "multi-user.target" ];
    automountConfig = {
      TimeoutIdleSec = "600";
    };
  };

  in

  [
    (commonAutoMountOptions // { where = "/mnt/tomoyo"; })
    (commonAutoMountOptions // { where = "/mnt/kotomi"; })
  ];
}

Nix store on NFS

In a single-user setup (not on Nixos) the Nix store can be also exported over NFS (common in HPC clusters) to share package over the networks. The only requirement is to also pass local_lock=flock or local_lock=all as mount option to allow the nix packages to take locks on modifications. Example entry in fstab:

<host_or_ip>/nix /nix nfs nofail,x-systemd.device-timeout=4,local_lock=all 0 0

TODO: Why this? That seems extremely unsafe. This disables NFS locks (which apply to all NFS clients), and makes locks local, meaning a lock taken by one NFS client isn't seen by another, and both can take their locks. So this removes protection against concurrent writes, which Nix assumes.