Library / SDK
jacobsa/fuse avatar
jacobsa/fuse

jacobsa/fuse: a Go library for writing and mounting user-space file systems

A Go package for implementing a FUSE file system.

575 stars122 forksGoApache-2.0

At a glance

What is it?
The jacobsa/fuse package lets you implement a FUSE file system in Go and mount it, with the kernel-facing code inherited from bazil.org/fuse. The design puts the request semantics in a separate package so you write your file system logic against a documented interface rather than against raw kernel messages.
Who is it for?
Adopt jacobsa/fuse if you are writing a Go program that needs to expose a virtual file system to the kernel, and you want the request semantics documented rather than reverse engineered. Do not adopt it as a general purpose FUSE framework with an active plugin ecosystem, because the package is a focused library with a small dependency list and no tagged releases on GitHub.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 113 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A FUSE library with three packages and a clear division of labour

This package allows for writing and mounting user-space file systems from Go, and it is installed like any other Go module:

bash
    go get -u github.com/jacobsa/fuse

After that you are pointed at three packages, and the split between them is the design.

The fuse package provides support for mounting a new file system and reading requests from the kernel. The fuseops package enumerates the supported requests from the kernel and provides documentation on their semantics. And the fuseutil package, in particular its FileSystem interface, provides a convenient way to create a file system type and export it to the kernel via fuse.Mount.

That division is the part worth pausing on. In a FUSE implementation, the hard part is rarely your file system logic. It is knowing what the kernel is asking for, what the reply has to contain, which fields the kernel checks, and what happens on an error. fuseops takes that part, enumerates the requests and documents what each one means, and fuseutil gives you an interface that maps onto them.

So the workflow is: you implement a type satisfying the FileSystem interface, you call fuse.Mount with it, and the kernel's requests arrive at your methods. Everything above that line is your code.

The library is Apache-2.0 licensed, the module path is github.com/jacobsa/fuse, it requires Go 1.23.0 according to the manifest, and the last push was on 2026-07-03. The repository publishes no GitHub releases.

The kernel-facing code descends from bazil.org/fuse

The README states the lineage directly: this package owes its inspiration and most of its kernel-related code to bazil.org/fuse.

That is the single most useful fact for anyone evaluating the library, because it tells you where to look when something is unclear. bazil.org/fuse is a long-standing FUSE implementation for Go, and the practical consequence is that a question about a specific kernel request or a specific error code usually has an answer in the upstream project or in the FUSE kernel documentation, not only in this repository.

It also tells you the character of the code. This is a library that grew out of a working implementation rather than one designed from scratch on paper, which is why the file names in the root are the ones you would expect from that history. There is connection.go for the connection to the kernel, conversions.go for translating between representations, ops.go for the operations, errors.go for error handling, mount.go, mount_linux.go and mount_darwin.go for the platform specific mounting, unmount.go, unmount_linux.go and unmount_std.go for the teardown, notifier.go for invalidation notifications, writev.go for the vectored write path, wirelog.go for logging the wire protocol, and freelists.go for buffer management.

The platform split is worth noting separately. Mounting is different on Linux, where FUSE is mounted through the fusermount helper and the /dev/fuse device, and on Darwin, where it goes through the macOS equivalent, and unmounting has a Linux path and a standard Unix path. The library handles that so you do not have to, but it means the platform behaviour is in specific files rather than spread through the code.

The samples directory is a catalogue of file system behaviours

The README says to make sure to also see the sub-packages of the samples package for examples and tests, and the directory listing is the largest part of the repository. It reads like a table of file system behaviours rather than a set of tutorials.

There is hellofs, which is the smallest useful thing. There is memfs, a file system backed by memory, and there are separate mount programs for it, mount_memfs and mount_hello. There is cachingfs, which is a file system that caches what it reads, and roloopbackfs, a read-only file system that loops back on itself, with a mount_roloopbackfs alongside it. There is readbenchfs with mount_readbenchfs, which reads as the name suggests.

The more unusual ones are the interesting ones. errorfs is a file system that deliberately fails, which is what you use to test how your code handles a kernel request going wrong. flushfs exercises the flush operation. forgetfs is named after the FUSE forget operation, which is how the kernel tells a file system it can drop a cached node. interruptfs handles interrupted requests, and notify_inval with mount_notify_inval deals with cache invalidation. statfs covers the statfs operation.

There are also two entry points that are not file systems at all: in_process.go and subprocess.go. That pair is a design decision rather than a sample. One demonstrates mounting and running a file system inside the same process, the other demonstrates doing it in a separate process, and having both present makes it easy to see which parts of your code need to change when you move from one to the other.

The library also ships a fusetesting/ directory, a test harness for file system implementations, and the go.mod lists a dependency on github.com/jacobsa/oglematchers, which is a matcher library, so the testing support is a first class part of the package rather than an afterthought.

A deliberately small dependency list

The dependency list in go.mod is short, and each entry tells you something about the design.

There are two direct dependencies that are not standard library or x/ packages: github.com/detailyang/go-fallocate, a Go implementation of the fallocate system call, and github.com/kylelemons/godebug, a package for pretty printing diffs in tests. The rest of the direct list is four packages from the same author, jacobsa/oglematchers for test matchers, jacobsa/ogletest as the test framework, jacobsa/syncutil for synchronisation helpers and jacobsa/timeutil for time helpers, plus golang.org/x/net and golang.org/x/sys.

The four same-author packages are the clearest evidence of the bazil.org/fuse lineage. Those helpers are the pieces that came across with the code rather than being part of the Go standard library.

golang.org/x/sys is the interesting one for a FUSE library, because it is how you get at the raw system calls, the ioctl numbers, the mount options and the platform specific structures that the standard library does not expose. The presence of x/net and x/sys and the absence of anything heavier is the practical benefit: adding this library to a Go program does not drag a large dependency tree into your build.

The golang.org/x/net version and the x/sys version in the manifest are recent enough that the library is being kept in step with current Go releases, and the go directive of 1.23.0 sets the floor for the language version you need.

What a FUSE library is for, and where the difficulty sits

It is worth being explicit about what writing a user-space file system means, because the reason to reach for this library rather than a command line tool is specific.

A FUSE file system is a program that answers kernel requests on behalf of a filesystem. The kernel sends it an open, a read, a write, a getattr, a lookup, and the program replies with the data or an error. The filesystem appears in the mount table and looks like any other directory, so every existing tool can use it without knowing anything about it.

The applications are specific. A file system that is a view of a remote object store, or of an archive, or of a database, or that is a debugging tool showing what a program is doing to its files. The reason to write one in Go rather than shelling out to a tool is that the program doing the file system work and the program that consumes it are the same program, in the same language, sharing types and error handling.

The difficulty sits in the protocol rather than the logic. The kernel expects particular reply structures, and getting a field wrong means an error that surfaces as a strange message rather than a clear failure. The unmounted directory then fails in ways that are hard to trace back to a missing structure field. This is exactly why fuseops exists as a separate package with documentation on semantics, and why the errorfs and flushfs samples exist.

The alternative approach is not to use FUSE at all, but to expose your data through a protocol the application already speaks, such as a local HTTP server or a database client. That is simpler and it avoids the kernel entirely. The trade is that the consumers have to be written for that protocol, whereas a FUSE file system is transparent to anything that opens a path.

Editorial conclusion

Adopt jacobsa/fuse if you are writing a Go program that needs to expose a virtual file system to the kernel, and you want the request semantics documented rather than reverse engineered. Do not adopt it as a general purpose FUSE framework with an active plugin ecosystem, because the package is a focused library with a small dependency list and no tagged releases on GitHub. Before you start, read the fuseops package documentation, because that is where the semantics of each supported request are described, and note that the mount and unmount paths are platform specific, with separate files for Linux, Darwin and the standard Unix implementation.

Frequently asked questions

How do I install the jacobsa/fuse package?

Run `go get -u github.com/jacobsa/fuse`. After that you use three packages: fuse for mounting a file system and reading requests from the kernel, fuseops for the enumeration of supported requests and their semantics, and fuseutil, whose FileSystem interface gives you a way to create a file system type and export it via fuse.Mount.

What examples does the jacobsa/fuse repository include?

The samples package includes hellofs, memfs, cachingfs, roloopbackfs, readbenchfs, errorfs, flushfs, forgetfs, interruptfs, notify_inval and statfs, each with mount programs where relevant, plus in_process.go and subprocess.go showing the two ways of running a file system. The fusetesting directory provides a test harness for file system implementations.

What are the dependencies of jacobsa/fuse?

The manifest requires Go 1.23.0 and lists go-fallocate, godebug, four same-author helper packages for test matchers, testing, synchronisation and time, plus golang.org/x/net and golang.org/x/sys. There is no large external dependency tree.

Is jacobsa/fuse based on another FUSE library?

Yes. The README states that the package owes its inspiration and most of its kernel-related code to bazil.org/fuse, which is the upstream project to consult for details of specific kernel requests and error handling.

Official sources

  1. Issues
  2. jacobsa/fuse on GitHub
  3. License: Apache-2.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jacobsa-fuse.svg)](https://hysenlabs.com/projects/jacobsa-fuse)

Was this useful?