SSHMount is a macOS menu bar app + FSKit extension + CLI for mounting remote directories over SFTP.
The project is configured to resolve SSH connection details from ~/.ssh/config host aliases.
- macOS 26+
- Xcode 16+
- Homebrew
libssh2 - Host aliases in
~/.ssh/config
Example:
Host my-server
HostName 192.168.1.100
User username
Port 22
IdentityFile ~/.ssh/id_ed25519App/: menu bar host appExtension/: FSKit filesystem extensionCLI/:sshmountcommand-line toolShared/: shared models/parsingproject.yml: XcodeGen project definition
- Install dependencies:
brew install libssh2 xcodegen- Generate Xcode project:
xcodegen generate- Open and build:
open SSHMount.xcodeprojOr build from terminal:
xcodebuild -project SSHMount.xcodeproj -scheme SSHMount -configuration Debug -derivedDataPath .derivedData buildDuring development, FSKit can be unstable and may leave the extension in a dangling state (e.g., "Resource busy" errors after failed mounts). If you encounter issues after reinstalling the extension, restart the FSKit daemons:
sudo pkill -9 fskitd && pkill -9 fskit_agentThis kills both:
fskitd(system daemon, needs sudo) - clears cached resource statefskit_agent(per-user daemon) - handles user-level operations
launchd will automatically respawn both daemons. This is often necessary when testing mount/unmount cycles.
Before daemon restarts, try force unmount first (sshmount unmount --force <mountPoint> or the app's Force unmount action).
Create a mount from:
Host(must exist in~/.ssh/config)Remote Path- typed
Connection Options(profile, workers, health, queue timeout, cache)
The app automatically tries SSH key authentication first. If keys fail, you'll be prompted for a password (used only for that mount attempt, never saved).
If a mount is stuck, use the row's Force unmount action (filled eject icon), which runs umount -f.
Commands:
sshmount mount <hostAlias>:<remotePath> <localMountPoint> \
--profile <standard|git> \
--read-workers <1-8> \
--write-workers <1-8> \
--io-mode <blocking|nonblocking> \
--health-interval <1-300> \
--health-timeout <1-120> \
--health-failures <1-12> \
--busy-threshold <1-4096> \
--grace-seconds <0-300> \
--queue-timeout-ms <100-60000> \
--cache-attr <0-300> \
--cache-dir <0-300>
sshmount unmount <localMountPoint>
sshmount unmount --force <localMountPoint>
sshmount list
sshmount status
sshmount test <hostAlias>:<remotePath>Example:
sshmount mount my-server:~/project ~/Volumes/my-server \
--profile standard \
--read-workers 1 \
--write-workers 1 \
--io-mode blocking \
--health-interval 5 \
--health-timeout 10 \
--health-failures 5 \
--busy-threshold 32 \
--grace-seconds 20 \
--queue-timeout-ms 2000 \
--cache-attr 5 \
--cache-dir 5Unmount:
sshmount unmount ~/Volumes/my-server
sshmount unmount --force ~/Volumes/my-serverOnly canonical typed options are supported:
profileread_workerswrite_workersio_modehealth_interval_shealth_timeout_shealth_failuresbusy_thresholdgrace_secondsqueue_timeout_mscache_attr_scache_dir_s
Legacy comma-separated mount option syntax is intentionally unsupported.
Recommended starting point for stable links:
--profile standard --read-workers 1 --write-workers 1 --io-mode blocking --health-interval 5 --health-timeout 10 --health-failures 5 --busy-threshold 32 --grace-seconds 20 --queue-timeout-ms 2000 --cache-attr 5 --cache-dir 5For Git-heavy workflows:
--profile gitThe git profile forces single-session I/O, disables attribute/directory caches, and performs a close-time SFTP fsync. If the server does not support SFTP fsync, close operations will fail instead of silently downgrading consistency guarantees.
For repositories mounted over SFTP/SSHFS, Git metadata writes can still be unreliable depending on server/filesystem behavior. The git profile improves consistency, but it is slower and depends on remote SFTP fsync support.
For best reliability, run Git write operations directly on the server:
ssh <hostAlias> 'cd /path/to/repo && git status'