Sshfs.nvim
📡 sshfs.nvim integrates with Neovim, using SSH and SSHFS to manage remote systems as if they were your local files.
Install / Use
/learn @uhs-robert/Sshfs.nvimREADME
🕶️ What It Is & Why
sshfs.nvim mounts hosts from your SSH config and makes them feel local.
You can browse, search, run commands, or open SSH terminals across multiple mounts without changing your workflow.
It stays lightweight and modern: no forced dependencies.
Built for Neovim 0.10+ using the best of both sshfs and ssh in tandem with your existing tools.
https://github.com/user-attachments/assets/d6453878-f93b-429a-9f36-8580cd9ed889
<details> <summary>✨ What's New / 🚨 Breaking Changes</summary> <br/> <!-- whats-new:start --> <details> <summary>🚨 v2.0 Breaking Changes </summary> <h3>Config restructure with hooks</h3> <ul> <li><code>mounts.unmount_on_exit</code> → <code>hooks.on_exit.auto_unmount</code></li> <li><code>mounts.auto_change_dir_on_mount</code> → <code>hooks.on_mount.auto_change_to_dir</code></li> <li><code>ui.file_picker</code> → <code>ui.local_picker</code></li> <li><code>ui.file_picker.auto_open_on_mount</code> → <code>hooks.on_mount.auto_run</code></li> </ul> <h3>SSH-first ControlMaster required</h3> <ul> <li>Mounting now tries a non-interactive socket first, then opens an auth terminal. This passes all login responsibility to ssh which enables native support for the ssh authentication flow.</li> </ul> <h3><code>sshfs_options</code> format change</h3> <ul> <li><code>connections.sshfs_options</code> must be a key/value table (e.g., <code>{ reconnect = true, ConnectTimeout = 5 }</code>); string arrays are ignored.</li> <ul> <li>Strings/numbers render as <code>key=value</code></li> <li>Boolean <code>true/false</code> enables/disables options, the value of <code>nil</code> also disables</li> </ul> </ul> <h3>Commands, API, and keymap renames (aliases will be removed after January 15, 2026)</h3> <ul> <li><strong>Commands:</strong> The following have beep deprecated: <ul> <li><code>:SSHEdit</code> → <code>:SSHConfig</code></li> <li><code>:SSHBrowse</code> → <code>:SSHFiles</code></li> </ul> </li> <li><strong>API:</strong> The following have beep deprecated: <ul> <li><code>edit</code> → <code>config</code></li> <li><code>browse</code> → <code>files</code></li> <li><code>change_to_mount_dir</code> → <code>change_dir</code></li> </ul> </li> <li><strong>Keymaps:</strong> The following have beep deprecated: <ul> <li><code>open_dir</code> → <code>change_dir</code></li> <li><code>open</code> → <code>files</code></li> <li><code>edit</code> → <code>config</code></li> </ul> </li> </ul> </details> <!-- whats-new:end --> </details>✨ Features
- Uses your toolkit – Auto-detects snacks, telescope, fzf-lua, mini, oil, yazi, nnn, ranger, lf, neo-tree, nvim-tree, or netrw.
- Auth that sticks – ControlMaster sockets + floating auth handle keys/passwords/2FA once, then reuse for mounts, live search, terminals, git, or scp.
- Real SSH config support – Honors Include/Match/ProxyJump and all
ssh_configoptions viassh -G; optional per-host default paths. - On-mount hooks – Auto-run find/grep/live find/live grep/terminal or your own function after connecting.
- Live remote search – Stream
rg/findover SSH (snacks, fzf-lua, telescope, mini) while keeping mounts quiet. - Multi-mount aware – Connect to several hosts, clean up on exit, and jump between mounts with keymaps or commands.
- Command suite –
:SSHFiles,:SSHGrep,:SSHLiveFind/Grep,:SSHTerminal,:SSHCommand,:SSHChangeDir,:SSHConfig,:SSHReload. - Host-aware defaults – Optional global and per-host default paths so you can skip path selection on common servers.
- Modern Neovim – Built for 0.10+ with
vim.uvfor reliable jobs, sockets, and cleanup.
📋 Requirements
| Software | Minimum | Notes |
| ---------- | ------------- | ------------------------------------------------------------------------------------------------ |
| Neovim | >=0.10 | Requires vim.uv support |
| sshfs | any | sudo dnf/apt/pacman install sshfs or brew install sshfs |
| SSH client | any | OpenSSH with ControlMaster support (default). Socket directory created automatically if missing. |
| SSH config | working hosts | Hosts come from ~/.ssh/config |
[!NOTE] For Mac users, see the macOS setup steps below.
🍏 macOS Setup
To use sshfs.nvim on macOS, follow these steps:
-
Install macFUSE Download and install macFUSE from the official site: https://macfuse.github.io/
-
Install SSHFS for macFUSE Use the official SSHFS releases compatible with macFUSE: https://github.com/macfuse/macfuse/wiki/File-Systems-%E2%80%90-SSHFS
📦 Installation
Lazy.nvim (Recommended)
{
"uhs-robert/sshfs.nvim",
opts = {
-- Refer to the configuration section below
-- or leave empty for defaults
},
}
Packer.nvim
use {
"uhs-robert/sshfs.nvim",
config = function()
require("sshfs").setup({
-- Your configuration here
})
end
}
vim-plug
Plug 'uhs-robert/sshfs.nvim'
Then in your init.lua:
require("sshfs").setup({
-- Your configuration here
})
Manual Installation
- Clone the repository:
git clone https://github.com/uhs-robert/sshfs.nvim ~/.local/share/nvim/site/pack/plugins/start/sshfs.nvim
- Add to your
init.lua:
require("sshfs").setup({
-- Your configuration here
})
⚙️ Configuration
You can optionally customize behavior by passing a config table to setup().
[!NOTE] Only include what you want to edit.
Here's the full set of defaults for you to configure:
require("sshfs").setup({
connections = {
ssh_configs = { -- Table of ssh config file locations to use
"~/.ssh/config",
"/etc/ssh/ssh_config",
},
-- SSHFS mount options (table of key-value pairs converted to sshfs -o arguments)
-- Boolean flags: set to true to include, false/nil to omit
-- String/number values: converted to key=value format
sshfs_options = {
reconnect = true, -- Auto-reconnect on connection loss
ConnectTimeout = 5, -- Connection timeout in seconds
compression = "yes", -- Enable compression
ServerAliveInterval = 15, -- Keep-alive interval (15s × 3 = 45s timeout)
ServerAliveCountMax = 3, -- Keep-alive message count
dir_cache = "yes", -- Enable directory caching
dcache_timeout = 300, -- Cache timeout in seconds
dcache_max_size = 10000, -- Max cache size
-- allow_other = true, -- Allow other users to access mount
-- uid = "1000,gid=1000", -- Set file ownership (use string for complex values)
-- follow_symlinks = true, -- Follow symbolic links
},
control_persist = "10m", -- How long to keep ControlMaster connection alive after last use
socket_dir = vim.fn.expand("$HOME/.ssh/sockets"), -- Directory for ControlMaster sockets
},
mounts = {
base_dir = vim.fn.expand("$HOME") .. "/mnt", -- where remote mounts are created
},
global_paths = {
-- Optionally define default mount paths for ALL hosts
-- These appear as options when connecting to any host
-- Examples:
-- "~/.config",
-- "/var/www",
-- "/srv",
-- "/opt",
-- "/var/log",
-- "/etc",
-- "/tmp",
-- "/usr/local",
-- "/data",
-- "/var/lib",
},
host_paths = {
-- Optionally define default mount paths for specific hosts
-- These are shown in addition to global_paths
-- Single path (string):
-- ["my-server"] = "/var/www/html"
--
-- Multiple paths (array):
-- ["dev-server"] = { "/var/www", "~/projects", "/opt/app" }
},
hooks = {
on_exit = {
auto_unmount = true, -- auto-disconnect all mounts on :q or exit
clean_mount_folders = true, -- optionally clean up mount fold
