1# SSHFS File Systems {#sec-sshfs-file-systems}
2
3[SSHFS][sshfs] is a [FUSE][fuse] filesystem that allows easy access to directories on a remote machine using the SSH File Transfer Protocol (SFTP).
4It means that if you have SSH access to a machine, no additional setup is needed to mount a directory.
5
6[sshfs]: https://github.com/libfuse/sshfs
7[fuse]: https://en.wikipedia.org/wiki/Filesystem_in_Userspace
8
9## Interactive mounting {#sec-sshfs-interactive}
10
11In NixOS, SSHFS is packaged as `sshfs`.
12Once installed, mounting a directory interactively is simple as running:
13```ShellSession
14$ sshfs my-user@example.com:/my-dir /mnt/my-dir
15```
16Like any other FUSE file system, the directory is unmounted using:
17```ShellSession
18$ fusermount -u /mnt/my-dir
19```
20
21## Non-interactive mounting {#sec-sshfs-non-interactive}
22
23Mounting non-interactively requires some precautions because `sshfs` will run at boot and under a different user (root).
24For obvious reason, you can't input a password, so public key authentication using an unencrypted key is needed.
25To create a new key without a passphrase you can do:
26```ShellSession
27$ ssh-keygen -t ed25519 -P '' -f example-key
28Generating public/private ed25519 key pair.
29Your identification has been saved in test-key
30Your public key has been saved in test-key.pub
31The key fingerprint is:
32SHA256:yjxl3UbTn31fLWeyLYTAKYJPRmzknjQZoyG8gSNEoIE my-user@workstation
33```
34To keep the key safe, change the ownership to `root:root` and make sure the permissions are `600`:
35OpenSSH normally refuses to use the key if it's not well-protected.
36
37The file system can be configured in NixOS via the usual [fileSystems](#opt-fileSystems) option.
38Here's a typical setup:
39```nix
40{
41 system.fsPackages = [ pkgs.sshfs ];
42
43 fileSystems."/mnt/my-dir" = {
44 device = "my-user@example.com:/my-dir/";
45 fsType = "sshfs";
46 options =
47 [ # Filesystem options
48 "allow_other" # for non-root access
49 "_netdev" # this is a network fs
50 "x-systemd.automount" # mount on demand
51
52 # SSH options
53 "reconnect" # handle connection drops
54 "ServerAliveInterval=15" # keep connections alive
55 "IdentityFile=/var/secrets/example-key"
56 ];
57 };
58}
59```
60More options from `ssh_config(5)` can be given as well, for example you can change the default SSH port or specify a jump proxy:
61```nix
62{
63 options =
64 [ "ProxyJump=bastion@example.com"
65 "Port=22"
66 ];
67}
68```
69It's also possible to change the `ssh` command used by SSHFS to connect to the server.
70For example:
71```nix
72{
73 options =
74 [ (builtins.replaceStrings [" "] ["\\040"]
75 "ssh_command=${pkgs.openssh}/bin/ssh -v -L 8080:localhost:80")
76 ];
77
78}
79```
80
81::: {.note}
82The escaping of spaces is needed because every option is written to the `/etc/fstab` file, which is a space-separated table.
83:::
84
85### Troubleshooting {#sec-sshfs-troubleshooting}
86
87If you're having a hard time figuring out why mounting is failing, you can add the option `"debug"`.
88This enables a verbose log in SSHFS that you can access via:
89```ShellSession
90$ journalctl -u $(systemd-escape -p /mnt/my-dir/).mount
91Jun 22 11:41:18 workstation mount[87790]: SSHFS version 3.7.1
92Jun 22 11:41:18 workstation mount[87793]: executing <ssh> <-x> <-a> <-oClearAllForwardings=yes> <-oServerAliveInterval=15> <-oIdentityFile=/var/secrets/wrong-key> <-2> <my-user@example.com> <-s> <sftp>
93Jun 22 11:41:19 workstation mount[87793]: my-user@example.com: Permission denied (publickey).
94Jun 22 11:41:19 workstation mount[87790]: read: Connection reset by peer
95Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Mount process exited, code=exited, status=1/FAILURE
96Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Failed with result 'exit-code'.
97Jun 22 11:41:19 workstation systemd[1]: Failed to mount /mnt/my-dir.
98Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Consumed 54ms CPU time, received 2.3K IP traffic, sent 2.7K IP traffic.
99```
100
101::: {.note}
102If the mount point contains special characters it needs to be escaped using `systemd-escape`.
103This is due to the way systemd converts paths into unit names.
104:::