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:::