Quick Start
Install mountx, test a driver, and create a mount.
#Install
npm i mountxWarning
Pre-1.0 API. Breaking changes can occur. After you mount a driver, every program on the host can reach it.
#Step 0 — see one without writing one
The package includes a command-line interface (CLI). It mounts a prepared in-memory filesystem and prints each kernel request. You do not need root on Linux or macOS.
npx mountx # mounts ~/mountx
npx mountx /tmp/scratch -t nfs # somewhere else, over a named transport
npx mountx --helpOpen another terminal. Run ls -l ~/mountx and cat ~/mountx/README.md. The first terminal shows the requests. See The mountx CLI for all flags.
#Step 1 — no mounting at all
Start with createLoopback(). It lets you use a driver like fs/promises in the current process.
import { createLoopback } from "mountx";
import { createMemoryDriver } from "mountx/drivers/memory";
const fs = createLoopback(createMemoryDriver());
await fs.mkdir("/notes");
await fs.writeFile("/notes/hello.txt", "hi");
const entries = await fs.readdir("/notes", { withFileTypes: true });
console.log(entries.map((entry) => entry.name)); // [ "hello.txt" ]
const bytes = await fs.readFile("/notes/hello.txt"); // Uint8Array
console.log(new TextDecoder().decode(bytes)); // "hi"createLoopback performs the same preparation as a real mount. It normalizes paths, returns ENOSYS for missing driver methods, and resolves capabilities. It does not create a mount and needs no root. It works on every platform, including Windows. Use it to develop and test drivers.
#Step 2 — mount it for real
Use the same driver as a directory that other programs on the host can open:
import { mount } from "mountx/auto";
import { createMemoryDriver } from "mountx/drivers/memory";
await using mounted = await mount(createMemoryDriver(), "/mnt/point");
mounted.transport; // "fuse" | "9p" | "nfs" — which one this host got
// /mnt/point is a real folder now. Keep the process alive.
await new Promise(() => {});Run the server:
node serve.tsUse the mount from another terminal. This requirement is important. See Do not use it from the same process.
echo hi > /mnt/point/hello.txt
cat /mnt/point/hello.txt
ls -l /mnt/pointPress Ctrl-C in the server terminal to remove the mount. await using also unmounts when its block ends. You can call await mounted.unmount() directly.
mount() from mountx/auto selects a usable transport for the host. It prefers FUSE on Linux and selects NFSv3 on macOS. The same code works on both platforms.
#Step 3 — serve a real folder
The node-fs driver sends requests to a real directory. It resolves each path component itself, so a request cannot reach data outside the root.
import { mount } from "mountx/auto";
import { createNodeFsDriver } from "mountx/drivers/node-fs";
await using mounted = await mount(createNodeFsDriver("/home/me/data"), "/mnt/point");You can use this driver directly. You can also wrap it to add caching, logging, filtering, or data transformation.
#Do I need root?
Serving does not need privileges. Attaching the mount to a directory can need privileges. The requirement depends on the host and transport.
| host | transport | root? |
|---|---|---|
Linux with fusermount3 | FUSE | no |
| Linux without it | FUSE | yes |
| Linux without FUSE, with 9P | 9P | yes, always — 9P has no rootless path |
| macOS | NFSv3 | no, but you must own the mount point |
| Linux (NFS pinned explicitly) | NFSv3 | yes |
On Linux, Node cannot make the mount system call. mountx asks fusermount3 to create the mount and return the connection. fusermount3 is the set-user-ID helper included with FUSE.
Most desktop Linux systems already include it.
Otherwise, install it with apt install fuse3 or dnf install fuse3.
When the process runs as root, mountx opens /dev/fuse and uses mount(8) directly.
9P has no helper like fusermount3, so it always needs root. For this reason, mountx/auto checks 9P only after FUSE. 9P provides real open and release state, which NFS does not provide.
On macOS, mountx uses NFSv3. /sbin/mount_nfs is not set-user-ID.
BSD systems let a user mount on a directory that the user owns.
You do not need sudo when you own the mount point.
FUSE and 9P do not apply on macOS. macFUSE is a third-party kernel extension with a different protocol.
BSD kernels do not have a v9fs client.
An unprivileged FUSE mount has two additional limits. allowOther needs user_allow_other in /etc/fuse.conf. Also, an unprivileged process cannot use umount -f to force an unresponsive mount down.
#Don't use it from the same process
A synchronous fs call to a mount served by the same process causes a deadlock.
A large number of concurrent asynchronous calls can also stop progress.
Put the client in another process, such as a shell or test child process.
See Troubleshooting.
#Where to go next
- Writing a driver provides a complete filesystem in about 60 lines.
- Mounting explains the mount object, lifecycle, and safe unmount.
- Tuning explains cache settings that can improve speed by 10–15 times.
- Troubleshooting covers stale mounts and deadlocks.