📂 SSHFS: Mount Remote Folders over SSH

Install, use and lock down SSHFS, with a restricted read-only account

Video: SSHFS (sysadmin video)
Prefer to watch? This video runs the guide's main commands on a real Ubuntu 24.04 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube. It is part of the Services, Storage and Security playlist.
SSHFS: mount a folder from another machine over SSH, and lock it down properly
sshfs user@server:/path ~/mnt

SSHFS makes a folder on a remote machine appear as a normal folder on yours. You edit, copy and search the files with your usual tools, and every byte travels through an ordinary SSH connection. There is nothing to install or open on the server beyond SSH itself.
This guide covers installing it, everyday use, permanent mounts, the problems that bite, and (the part most guides skip) how to give it a restricted, read-only account so that "mount my web folder" does not mean "give this machine a shell on my server".
Tested with SSHFS 3.7.3 and FUSE 3.14 on Ubuntu 24.04 as the client, mounting from an Ubuntu 24.04 server running OpenSSH. Option names were checked against the sshfs manual page.

When to use it (and when not)

Good for:   editing files on a server with a local editor, browsing logs and configs, quick access to a few folders, letting a tool read a live web root.
Not good:   databases, anything that needs file locking to work between machines, build trees or git checkouts with thousands of small files, heavy bulk copying.
Instead:    bulk or repeated copying: rsync.  Many clients or a fast LAN share: NFS or Samba.  Cloud storage: rclone.
Remember:   every file operation is a network round trip. It feels instant on a LAN and sluggish on a slow link.

How the pieces fit together

Client (your workstation)   needs the sshfs package (it uses FUSE, a way to build filesystems in user space)
Server (the remote machine) needs only a working SSH server with SFTP enabled, which is the default. Install NOTHING extra there.
The classic mistake is installing sshfs on the SERVER. The client does the mounting, the server just answers SFTP requests. If you did it by accident, it is harmless: remove it with your package manager and carry on.
Check that SFTP is enabled on the server:   grep -i '^Subsystem' /etc/ssh/sshd_config      (expect a line starting with: Subsystem sftp)

Install it (on the machine that will do the mounting)

Ubuntu / Debian:                sudo apt install sshfs
Fedora:                         sudo dnf install fuse-sshfs
RHEL / Rocky / Alma:            enable EPEL first (sudo dnf install epel-release), then: sudo dnf install fuse-sshfs
Arch:                           sudo pacman -S sshfs
macOS (third-party):            macFUSE plus an sshfs build; there is no official Apple support
Windows (third-party):          WinFsp plus SSHFS-Win, which maps a drive letter
Check:                          sshfs --version                      (prints the sshfs and FUSE versions)

Everyday use

1. Make an empty folder you own to be the mount point:
   mkdir -p ~/mnt/server
2. Mount (it asks for the SSH password or key passphrase):
   sshfs user@192.168.1.20:/var/www ~/mnt/server
3. Use it like any folder:
   ls ~/mnt/server
   mount | grep sshfs                                           # shows it, and whether it is ro (read-only) or rw
4. Unmount when you are done:
   fusermount3 -u ~/mnt/server                                  # FUSE 3 (current distributions). Older systems: fusermount -u
   umount ~/mnt/server                                          # also works on many systems
Mount the folder you need, not the whole server. "/" works, but you then see (and can break) everything your account can reach.
A different SSH port: add -p 2222.   Compression, useful on slow links only: add -C.

Use a key instead of a password

ssh-keygen -t ed25519                                           # make a key (give it a passphrase)
ssh-copy-id user@server                                         # install the public key on the server
sshfs user@server:/path ~/mnt/server                            # no password prompt now
Keep your connection settings in ~/.ssh/config and the command gets short:
   Host web
       HostName 192.168.1.20
       User deploy
       IdentityFile ~/.ssh/id_ed25519
       IdentitiesOnly yes
   Then:   sshfs web:/var/www ~/mnt/server
The first connection asks you to trust the server's fingerprint. Compare it with the real one (on the server: ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub) before typing yes.

Options worth knowing (-o option,option,...)

reconnect                             reconnect automatically if the connection drops (files that were open must be re-opened)
ServerAliveInterval=15                send a keep-alive every 15 seconds so a dead link is noticed (with the default count of 3, after about 45 seconds)
ro                                    mount read-only on YOUR side (a good habit; see the lock-down section for enforcing it on the server too)
IdentityFile=~/.ssh/key               which key to use (add IdentitiesOnly=yes so SSH offers only that key)
default_permissions                   make the local kernel enforce the file permission bits
idmap=user                            show the remote user's files as owned by you (the default, none, shows the remote numeric IDs)
allow_other                           let other local users (and root) see the mount; needs user_allow_other in /etc/fuse.conf, and think before enabling it
follow_symlinks                       follow symbolic links on the server (by default links are shown as links, which may not resolve locally)
delay_connect                         do not connect until the folder is first used (handy in scripts and fstab)
dir_cache=yes|no, dcache_timeout=N    directory listings are cached for a short time; set dir_cache=no if you need to see changes made elsewhere instantly
max_conns=N                           use several SSH connections to keep the mount responsive during big transfers
sshfs_sync                            synchronous writes: slower but safer
-d                                    debug output (stays in the foreground): the first thing to try when a mount fails

A full example:
sshfs -o ro,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,IdentityFile=$HOME/.ssh/id_ed25519,IdentitiesOnly=yes,default_permissions user@server:/var/www ~/mnt/server

Mount it permanently (at login or on first use)

Add a line to /etc/fstab. Mounts at boot run as root, so use a key and known_hosts entry that root can read, and make the folder available only when first used:
user@server:/var/www  /mnt/server  fuse.sshfs  noauto,x-systemd.automount,_netdev,reconnect,ServerAliveInterval=15,IdentityFile=/root/.ssh/id_ed25519,ro,allow_other,delay_connect  0  0
   noauto + x-systemd.automount   the folder mounts the first time something opens it, not at boot (a missing server cannot slow the boot)
   _netdev                        marks it as a network mount
Create the mount point, then test:   sudo mkdir -p /mnt/server && sudo systemctl daemon-reload && ls /mnt/server
Add the server to root's known hosts first, or the mount silently fails:   sudo ssh user@server true
Do the manual mount first and only move it to fstab when it works.

Permissions and ownership

You see the files as the SSH account on the server sees them, and you can do what that account can do, no more. The server's file permissions are the real rules.
Typical surprises: files appear owned by a number (use idmap=user); a file is "Permission denied" because the account is not in the right group on the server; new files are owned by the account you log in as.
SSHFS cannot give you more access than the account has. Which brings us to the most important part.

Lock it down: a restricted account for the mount

If you mount with your normal admin login, the machine that mounts it effectively holds a full SSH key to your server. For a mount that only needs to read a website folder, that is far too much. Instead, make a dedicated account that can do exactly one thing: read (or, when needed, write) one folder over SFTP.
What this achieves:
   * no shell, no commands, no port forwarding, no password login
   * the key works only from your workstation's IP address
   * the account is "chrooted": it sees the target folder as its entire world (no /etc, no /home, no logs)
   * read-only on the server itself, so even a hacked workstation cannot change your files
On the server:
1. Create the account (no shell, no password, no home) and a root-owned place for its key:
   sudo adduser --system --group --no-create-home --home /nonexistent --shell /usr/sbin/nologin sshfsuser
   sudo mkdir -p /etc/ssh/authorized_keys.d
2. A chroot directory must be owned by root and not writable by anyone else, and so must every folder above it:
   ls -ld /var /var/www                                        # both must show root root and drwxr-xr-x
3. On the workstation make a dedicated key (ssh-keygen -t ed25519 -f ~/.ssh/sshfsuser_ed25519) and find its source address:
   ip route get 192.168.1.20 | grep -o 'src [0-9.]*'
4. Install the public key with two safety options, from= (only that address may use it) and restrict (no forwarding, no terminal, no extras). One line:
   from="192.168.1.50",restrict ssh-ed25519 AAAA... comment
   saved in /etc/ssh/authorized_keys.d/sshfsuser (owned by root, mode 644). Keeping the file outside the user's home means the account cannot change its own keys.
5. Add the SSH rules in a drop-in file, /etc/ssh/sshd_config.d/60-sshfsuser.conf:
   Match User sshfsuser
       AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u
       AuthenticationMethods publickey
       PasswordAuthentication no
       KbdInteractiveAuthentication no
       ChrootDirectory /var/www
       ForceCommand internal-sftp -R -u 022
       AllowTcpForwarding no
       AllowAgentForwarding no
       X11Forwarding no
       PermitTTY no
       PermitTunnel no
   ChrootDirectory is what confines the account to /var/www. internal-sftp is OpenSSH's built-in file server (it needs no shell and no files inside the chroot). -R makes the SERVER refuse all writes. -u 022 makes any file it creates later readable by the web server.
6. Check, then apply (applying a reload does not drop your existing SSH sessions, so keep one open):
   sudo sshd -t
   sudo sshd -T -C user=sshfsuser,host=x,addr=192.168.1.50 | grep -Ei 'chroot|forcecommand|authorizedkeysfile'
   sudo sshd -T | grep -i '^allowusers'                       # if this prints a line, add sshfsuser to it or the account is refused
   sudo systemctl reload ssh
Test BEFORE mounting, from the workstation:
   ssh -i ~/.ssh/sshfsuser_ed25519 sshfsuser@192.168.1.20            # must be refused, or say the service allows sftp only
   ssh -i ~/.ssh/sshfsuser_ed25519 sshfsuser@192.168.1.20 id         # must NOT print uid=...
   sftp -i ~/.ssh/sshfsuser_ed25519 sshfsuser@192.168.1.20           # then: pwd (shows /), ls, cd .. (stays at /), ls /etc (nothing), put somefile (fails)
Mount it read-only on both ends:
   sshfs -o ro,IdentityFile=$HOME/.ssh/sshfsuser_ed25519,IdentitiesOnly=yes,reconnect,ServerAliveInterval=15,default_permissions sshfsuser@192.168.1.20:/ ~/mnt/server
Here the remote path "/" means the chroot (/var/www on the server).
Write access, temporarily, for one folder (for example when a tool must deploy files):
   * give the account write permission on that folder only, using ACLs:   sudo setfacl -R -m u:sshfsuser:rwX /var/www/html
   * remove -R from the ForceCommand line, sshd -t, reload ssh, remount without ro
   * when done, put it all back: add -R again, sudo setfacl -R -x u:sshfsuser /var/www/html, reload, remount with ro
   * take a backup first: sudo tar czf ~/site-backup-$(date +%Y%m%d).tgz -C /var/www/html .
Things the chroot changes: symbolic links that point outside the chroot cannot be followed, and the account can read only what is world-readable or explicitly allowed to it (hide a folder from it with sudo setfacl -R -m u:sshfsuser:--- /var/www/dashboard).

When it hangs or breaks

Mount hangs after a network drop       By default sshfs has no timeouts, so programs touching the mount can freeze. Mount with -o reconnect,ServerAliveInterval=15 to avoid it. If it is already frozen: kill the sshfs process for that mount (find it with: pgrep -a sshfs, then: kill PID; pkill -x sshfs would stop ALL your sshfs mounts), then fusermount3 -u the folder. The manual page notes that fusermount -zu (a lazy unmount) does NOT unfreeze programs that are already blocked.
Transport endpoint is not connected    The connection died. fusermount3 -u ~/mnt/server, then mount again.
Permission denied (publickey)          Wrong key, key line split over two lines, the from= address is not your current IP, or an AllowUsers line excludes the account. On the server: sudo journalctl -u ssh -n 25 --no-pager
"bad ownership or modes for chroot directory"   /var or the chroot folder is not root-owned, or is writable by group or others. Fix the owner and mode; this is a hard rule of ChrootDirectory.
fusermount: option allow_other only allowed if 'user_allow_other' is set in /etc/fuse.conf    Uncomment user_allow_other in /etc/fuse.conf, or drop allow_other.
Mount works but folder looks empty     You mounted the wrong path, or the account cannot read it. Try ls -ld on the server as that account; use sshfs -d for a debug trace.
File changes made on the server do not show up   Directory caching: add -o dir_cache=no, or wait for the cache timeout.
Everything is very slow                Many small files over a high-latency link. Use rsync for bulk copies, or work on fewer files at a time.
fusermount3: command not found         Old system: use fusermount -u.

Tips

Mount the folder you need, with the least privilege that does the job. Read-only is the default; write is an exception you switch on and then off.
Give the dedicated key a passphrase, and keep it in ssh-agent for the session.
Give the workstation a fixed address in your router. A from= restriction on a changing address is a good way to lock yourself out.
Put the mount and unmount commands in a small script or shell alias so you do not retype the option list.
Do not mount over something important. Mounting on top of a non-empty folder hides its contents until you unmount.
Never leave a write-enabled mount of a production folder lying around "just in case". Unmount it when the job is done.
Why not just give the tool SSH access? Because "can read the web folder" and "can run any command as me" are very different sentences. Write the first one into the configuration.
The server does not need sshfs installed. Ask me how I know.

Sources (checked 2026-10-01)

sshfs manual page (SSHFS 3.7.3):             man sshfs, or https://github.com/libfuse/sshfs
OpenSSH sshd_config (ChrootDirectory, ForceCommand, Match):   man sshd_config
OpenSSH SFTP server options (-R, -u):        man sftp-server
FUSE and fusermount:                         https://github.com/libfuse/libfuse