llama.cpp RPC · SSHFS · Tailscale · and everything that broke along the way
The model is 19.5 gigabytes. Split across five files. I need to run it on six VMs, each with 8 GB of RAM. The math doesn’t work out — but I did it anyway.
This is not a tutorial on the right way to do distributed inference. This is a field report on what actually happens when you throw a quantized 32-billion-parameter language model at a cluster of free-tier cloud VMs connected over a mesh VPN.
TL;DR: It works. llama.cpp’s RPC mode distributes model tensors across six nodes. A file-sync daemon ate our data twice. SSHFS had a mutex deadlock. Loading takes 45 minutes. Generation is 0.52 tokens per second. I’d do it again.
Six Linux VMs from a free compute provider, 8 GB RAM and 20 GB disk each. Connected via Tailscale in a full mesh VPN — no NAT traversal issues, clean peer-to-peer connections. The model: Qwen2.5-32B-Instruct at Q4_K_M quantization (19.8 GB total), split into five GGUF files downloaded from Hugging Face.
The inference engine: llama.cpp, specifically its RPC (Remote Procedure Call) mode — a relatively obscure feature that lets you distribute a model's tensors across multiple machines and run inference collaboratively.
llama.cpp's RPC model is elegant: a central coordinator (llama-server) reads the model files, shards tensors across multiple RPC workers (llama-rpc-server), and each worker holds its share in RAM.
Master coordinator distributes tensors via RPC to all 6 nodes. Parts 4+5 on m5, via SSHFS.
Qwen2.5-32B-Instruct weighs in at 19.8 GB at Q4_K_M — split into five GGUF files. Master only has 20 GB of disk, so parts 4 and 5 had to live on m5's local disk.
| File | Size | Location |
|---|---|---|
| ...00001-of-00005.gguf | 3.7 GB | master (local disk) |
| ...00002-of-00005.gguf | 3.7 GB | master (local disk) |
| ...00003-of-00005.gguf | 3.8 GB | master (local disk) |
| ...00004-of-00005.gguf | 3.7 GB | m5 → SSHFS mount |
| ...00005-of-00005.gguf | 3.8 GB | m5 → SSHFS mount |
This is the part nobody tells you about: the hidden daemon running in the background, quietly waiting to ruin your day.
The cluster came pre-configured with lsyncd — a live sync daemon that uses inotify to watch for file changes and rsync them immediately to all worker nodes. Its config had one line that destroyed us:
delete = true tells rsync to make the destination an exact mirror of the source. We downloaded parts 4 and 5 directly to m5's local disk. lsyncd saw they weren't on master, and obediently deleted them. We lost ~40 GB of downloads across multiple attempts before finding this.
The fix: systemctl --user stop lsyncd && systemctl --user disable lsyncd. It ran as a user systemd service — killing the process wasn't enough because systemd restarted it every time.
With lsyncd dead, SSHFS was the right tool — mount m5's filesystem over SSH, let llama-server read files normally. But under sustained IO load, the daemon would enter D state (uninterruptible sleep) and never return.
Root cause: libfuse3 3.17.2 signal handler deadlock. The signal handler tries to acquire se→lock which the FUSE main loop already holds. The fix: mask signals before exec-ing sshfs so the handler never runs.
libfuse3 3.17.2 signal handler deadlock — and the signal-masking fix
trap "" SIGNAL sets the handler to SIG_IGN in the shell, inherited by the child after exec. No handler means no deadlock. With direct_io, SSHFS achieves a steady 8.2 MB/s.
--load-mode none means: put nothing on master's CPU. All tensors go to the RPC workers. llama-server reads all five GGUF files, distributes tensor weights across the six workers' RAM, and then acts purely as a coordinator. The whole load takes about 48 minutes — dominated by reading parts 4+5 over SSHFS.
0.52 tokens per second. For a 20-token response, that's 38 seconds. Prompt eval is 9× faster (4.67 tok/s) because prefill is embarrassingly parallel, while autoregressive generation requires sequential forward passes — each one a full round-trip across all RPC workers via Tailscale.
delete = true will silently destroy any file that doesn't exist on the master. Always audit systemd user services on a managed cluster before writing any important data.0.52 tokens per second is not useful for real work. But it runs. And sometimes that's the point.
Written by Sai Nivedh · November 2026 · All experiments conducted on actual free-tier cloud VMs