Skip to content

About

Fast(est) Nextcloud Desktop client with proper VFS support on Linux. Passthrough cache, provides the server-based thumbnailer (RAW included), HTTP/3, parallel connections & threads... Optimzed for large media & creative teams, or your music/video library.

Topics

Resources

Stars

12 stars

Watchers

0 watching

Forks

Latest commit

 

History

853 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ncRS Desktop client

Unofficial Nextcloud Desktop client with proper VFS support on *nix. Rewritten from scratch in rust. Designed to perform well in installations with a very large amount of shared files/media etc (where cloud storage >> local storage), as well as in a smaller cloud.

Sync status and plugins Settings
NC Metadata in Nautilus Nextcloud-generated thumbnails even for RAW files

NOTE: personal info has been edited out from the screenshots, in real life it looks normal :)

Feature comparison:

Feature Nextcloud Desktop GNOME Integration/GVfs ncRS
Real Virtual Filesystem ❌ (Experimental, bad approach which doesn't work with shell/file pickers etc) ✅ (remote only) ✅
Streaming Download Support (play large 4K videos at network-rate) ❌ ❌ (yes, but throughput is lower) ✅
Nextcloud Notifications ✅ ❌ ✅
Nautilus integration: syncing/downloaded/shared ✅ (partial) N/A since it's only online ✅
Instant local access ✅ ❌ ✅
Fast time to first use (usable right after setup, no bulk download first) ❌ (must fully download the entire selected folder tree before anything is usable) ✅ ✅
Zero-copy cache reads (kernel serves the cached file directly, no extra copy through the app) ✅ (not really a fair comparison — it isn't virtual, so every file is a permanent full local copy, always "cached" by definition) ❌ ✅ (Linux 6.9+, via kernel FUSE passthrough)
Nextcloud-generated thumbnails ❌ ❌ ✅ (even for RAW images!)
Local cached file pruning ❌ ❌ ✅ intelligently keeps local copies of often-used files
Dynamically cache files/keep part locally ❌ ❌ (No internet = no files) ✅
No path conflicts ❌ (may emit sync errors) ✅ ✅ No dumb 'Some files could not be synced'
Maps nextcloud permissions to filesystem permissions ❌ ❌ (will error, but doesn't first display it to the user) ✅
Office document locking (Nextcloud Office/Collabora ↔ local LibreOffice, no conflicted copies) ⚠️ (since 3.10 it locks files opened in MS Office/LibreOffice, but users report locks not taking or not being released) ❌ ✅ locks a document on the server while LibreOffice, Office, Vim or Kate has it open, keeps the lock through saves, and shows files someone else has open in Collabora as read-only
HPB Support/Sync speed ✅ NC HPB ❌ ✅ NC HPB
QUIC/HTTP3 Support ❌ ❌ ✅
Syncing multiple OS paths -> different folders within Nextcloud ✅ ❌ ❌ (everything mounted under the Nextcloud path)

Server tips

Server-side recommendation: If you have shell access to your Nextcloud server, you shall enable background thumbnail pre-generation with occ preview:pre-generate.

This makes the server generate thumbnails during idle time rather than on-demand, which would eliminate the congestion entirely for directories that have been indexed.

We avoid local thumbnail generation so that entire files don't have to be downloaded for the local thumbnailer to run.

Note for coding agent users (Claude Code, Codex, Cursor, …)

Coding agents are not polite to network filesystems. To find a binary or a config file they happily run find / -name protoc, rg pattern ~ or du -sh ~/*. Those commands walk into ~/Nextcloud, and every directory they enter becomes a WebDAV request to your server. Claude Code does not kill a long command: after 2 minutes it moves it to the background, where a single find / can crawl your whole Nextcloud for hours (we measured ~20 listings/s and 20,000 folders in 20 minutes). Such a crawl can overload the server into returning 500 errors. ncrs defends itself: it backs off from a failing server, and it paces any single program that crawls folders it hasn't cached (Settings → Performance → Slow down folder crawlers, on by default at 10 folders/s). Still, the cheapest request is the one never sent.

Keep agents off the mount by adding the bundled guard hook to your user-level Claude Code settings. It refuses find/rg/grep -r/du/fd/ls -R/tree walks, and Glob/Grep tool searches, that would descend into the mount from / or ~. It allows them when they stay on one filesystem (find / -xdev, rg --one-file-system, du -x), prune the mount, or target a narrower directory. The refusal message tells the agent how to rewrite the command.

mkdir -p ~/.claude/hooks
curl -fsSLo ~/.claude/hooks/block-root-walks.py \
  https://raw.githubusercontent.com/rgon/ncrsDesktop/master/scripts/claude-code/block-root-walks.py
# Merge the hook into ~/.claude/settings.json (keeps your existing settings):
jq '.hooks.PreToolUse = ((.hooks.PreToolUse // []) + [{"matcher":"Bash|Glob|Grep","hooks":[{"type":"command","command":"python3 ~/.claude/hooks/block-root-walks.py","timeout":10}]}])' \
  ~/.claude/settings.json > /tmp/claude-settings.json && mv /tmp/claude-settings.json ~/.claude/settings.json

The hook guards ~/Nextcloud by default. To guard other mount points, set NCRS_WALK_GUARD_MOUNTS=/path/one:/path/two. Also consider adding a line like "never recurse from / or ~ without -xdev; ~/Nextcloud is a network mount" to your ~/.claude/CLAUDE.md. Other agents need their own equivalent, such as a command deny-list or rules file.

Usage

Download and install the .deb file from the /releases page. You may simply double click the .deb to install it with your OS's package manager.

openSUSE (Leap 16, Slowroll, Tumbleweed)

The .rpm packages the same client as the .deb (GUI tray app, daemon, GNOME search provider, CR3 thumbnailer, Nautilus extension, and the KF6 Dolphin plugin + ServiceMenu). It is built from source, so the build host needs cargo, pnpm, rpm-build and every BuildRequires listed in packaging/ncrs.spec. A bare opensuse/tumbleweed container does not have those installed — install them first (.github/workflows/ci.yml has the exact zypper install line CI uses):

bash scripts/build-rpm.sh            # -> dist/ncrs-<version>-0.<arch>.rpm

On a host whose toolchain is not installed as RPMs — rustup's cargo, or an npm-installed pnpm — rpmbuild cannot see them and stops with Failed build dependencies:. Install the -devel packages the spec needs (fuse3-devel, gtk3-devel, webkitgtk3-devel, kf6-kio-devel, librsvg-devel, plus cmake/qt6-base-devel/kf6-kcoreaddons-devel/kf6-extra-cmake-modules), then build with --nodeps:

bash scripts/build-rpm.sh --nodeps

--nodeps skips only rpmbuild's BuildRequires check; the libraries must still be present or the compile fails. CI installs the distro toolchain and does not use it.

Install it. The locally built artifact is not GPG-signed, so zypper warns that the package is unsigned and prompts for confirmation; pass --allow-unsigned-rpm to skip that prompt:

sudo zypper in ./dist/ncrs-*.rpm

The GUI tray app autostarts at login via /etc/xdg/autostart/es.rgon.ncrs.desktop, exactly like the .deb. The headless alternative is systemctl --user enable --now ncrs.service, which runs the daemon without the GUI; the two coexist (see Provisioning below).

The Nautilus sync-state emblems need python-nautilus. On Leap 16 it may not be in the OSS repo — enable the Package Hub for your release and install it from there if zypper cannot find it. The Dolphin integration ships inside the RPM, so it needs nothing extra.

On openSUSE Leap 16 the tray icon also needs libayatana-appindicator3-1, which is Package Hub–only (not in OSS/Update). Enable the Package Hub and install it to get the tray icon. It is a Recommends on Leap 16, not a hard dependency, so a stock install succeeds without it and the GUI runs — you just get no tray icon.

First-time setup

You may directly set it up using the GUI, following the interactive 'Authorize Device'-type login from the web browser. This issues a revocable Nextcloud app password, which is saved in your OS keyring (GNOME Keyring/KWallet).

The recommended configuration options are the defaults. However, you may modify them via the cogwheel icon in the application menu.

Non-interactive setup

ncrs-gui writes its configuration to a yaml file, for easy view and editing.

You may edit this file to configure it non-interactively, which is especially useful for remote/fleet deployment.

Create the config file — run the daemon once to generate the skeleton, then fill it in:

cargo run -p ncrs_core        # exits immediately, writes ~/.config/ncrs/config.yaml
nano ~/.config/ncrs/config.yaml

The file looks like this. You shall use an app password rather than the user's main password:

url: https://cloud.example.com/remote.php/dav/files/YOUR_USERNAME/
username: youruser
password: xxxx-xxxx-xxxx-xxxx   # app password (optional) or fetched/saved from the system keyring
mount_point: /home/<user>/ncrs
user: youruser

Provisioning (corporate / multi-user)

The .deb (built by scripts/build-deb.sh will allow fleet config. For

  • Config is per-user at ~/.config/ncrs/config.yaml (XDG; there is no system-wide config). A template ships at /usr/share/doc/ncrs/config.yaml.example, or generate one with ncrs --print-default-config.
  • Push per-user config files with your config-management tool (e.g. Ansible template to each user's ~/.config/ncrs/config.yaml). You may pre-fill /etc/skel/.config/ncrs/config.yaml so new accounts start provisioned. If setting the password on the config file, always use per-user app passwords or an auth_command — never a shared credential.
  • The GUI tray app autostarts at login via /etc/xdg/autostart/es.rgon.ncrs.desktop and runs the systemctl service.
  • Headless alternative: systemctl --user enable --now ncrs.service runs the daemon without the GUI. The two coexist: when the GUI starts and finds the service already serving the IPC socket, it attaches as a client — mirroring sync state, errors, and transfers in the tray and forwarding pause/resume — instead of mounting a second time. Quitting an attached tray will unmount it, however.

Advanced usage

Download the .deb and manually install it

cd Downloads
sudo apt install ./ncrs_*_amd64.deb

And open it from your applications list or the terminal ncrs-gui & disown

Dependencies (shall be automatically requested by the .deb)

sudo apt-get install fuse3 libfuse3-dev libxdo-dev

Development dependencies

The Rust toolchain: https://rustup.rs/

For the Nautilus extension (optional):

sudo apt-get install python3-nautilus

Development running

GUI + daemon (the normal way):

./runui.sh

This starts the Tauri tray app; the daemon mounts WebDAV at your configured mount_point automatically.

It will also install the shell integrations etc: ./shell_integration/file-managers/nautilus/install.sh to show sync-state emblems (cloud = remote-only, tick = local) on files in the mount.

File-browser support is modular: the service detects which browsers are installed (Nautilus, Dolphin, Nemo) and applies each one's profile (indexer exclusion, thumbnails, type detection). Profiles can be toggled from the GUI's File browsers settings or with ncrs-ctl integrations / ncrs-ctl integration-set <id> on|off|auto. Adapters talk to the service over the protocol in shell_integration/file-managers/PROTOCOL.md; the Dolphin adapter lives in shell_integration/file-managers/dolphin/.

Downloaded files are cached in ~/.cache/ncrs/.

Daemon only (headless / for systemd):

RUST_LOG=info cargo run -p ncrs_core

Kernel FUSE passthrough (zero-copy reads)

On Linux 6.9+ the kernel can serve reads of fully-cached files straight from the cache copy. Registering those files needs CAP_SYS_ADMIN, which only the small ncrs-passthrough helper gets (the packages install it at /usr/libexec/ncrs/ncrs-passthrough with cap_sys_admin=ep); the ncrs service itself runs with no capabilities, so it mounts through fusermount3. The service spawns the helper per mount and falls back to buffered reads when it is missing or lacks the capability (the GUI settings and the IPC PASSTHROUGH_STATUS then report it as unavailable). To try it on a development build, cap the helper next to your binary — never ncrs itself:

cargo build -p ncrs_core -p ncrs_passthrough
sudo setcap cap_sys_admin=ep target/debug/ncrs-passthrough

(NCRS_PASSTHROUGH_HELPER=/path/to/ncrs-passthrough overrides where the service looks for it.)

Uninstall extension:

rm ~/.local/share/nautilus-python/extensions/syncstate.py
nautilus -q

Running tests

Unit + integration tests (no server needed — integration tests skip gracefully):

cargo test
python3 -m unittest discover -s shell_integration/file-managers/nautilus -v

End-to-end tests against a real WebDAV server (requires Docker):

docker compose -f docker/docker-compose.yml up -d
WEBDAV_TEST_URL=http://localhost:8888 cargo test -p ncrs_core --test integration_test
docker compose -f docker/docker-compose.yml down

Package build + verification (what CI runs; --container needs Docker or Podman):

./scripts/build-deb.sh              # options: --version, --arch, --out-dir, --skip-gui, --skip-build
./scripts/test-deb.sh --container   # metadata, contents, desktop entries, clean-install smoke test

Cache and Storage Paths

ncrs stores locally-available files under ~/.cache/ncrs/<server-hash>/:

Directory Purpose Survives restart User-controlled
kept/ Files explicitly pinned via "Keep Locally" Yes Yes (KEEP/EVICT)
cache/ Files auto-downloaded during reads Yes (validated on boot) No (may be evicted)

The <server-hash> component is derived from the WebDAV URL to allow multiple server configurations.

Additional metadata files in the root:

  • dir_cache.json -- cached directory listings (etags + entries)
  • file_cache.json -- tracks which files are cached/kept locally (remote path, etag, kept flag)
  • journal.bin -- offline mutation journal for pending uploads/deletes/renames

Config options (in ~/.config/ncrs/config.yaml):

  • auto_keep_locally_modified_files: true -- keep a local cached copy after uploading a file you edited (default on; cleared by the cache size limit, auto-purge and "purge local cache")
  • auto_keep_cached_files: true -- promote read-cached files to kept automatically
  • cache_max_size_bytes: 34359738368 -- max size for cache/ directory (default 32 GB, 0 = unlimited). Oldest-accessed files are evicted first.
  • cache_auto_purge_days: 10 -- auto-delete cached files not accessed in N days (default 10, 0 = disabled)
  • cache_streamed_reads: true -- promote fully-streamed files to disk cache
  • read_ahead_bytes: 67108864 -- read-ahead window for streaming reads (default 64 MB)
  • cache_cleanup_interval_secs: 3600 -- how often to run cache pruning (default 3600 = 1 hour)
  • dir_cache_max_stale_mins: 15 -- before showing a directory whose cached listing is older than this, ask the server whether it changed, so the first listing is already current (default 15, 0 = disabled). An unchanged directory costs one small etag request. Only applies while push notifications are down; while they work, a 24-hour backstop applies.
  • keep_paths: ["/Documents", "/Photos"] -- remote paths to auto-keep locally on startup (default empty)

About

Fast(est) Nextcloud Desktop client with proper VFS support on Linux. Passthrough cache, provides the server-based thumbnailer (RAW included), HTTP/3, parallel connections & threads... Optimzed for large media & creative teams, or your music/video library.

Topics

Resources

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages