Afero: one interface between your code and every filesystem backend
The Universal Filesystem Abstraction for Go
At a glance
- What is it?
- Afero is a Go library that mirrors the os package behind an afero.Fs interface, so production code injects OsFs and tests inject MemMapFs. Its own repository ships no S3 backend, and the newest tag is older than the newest commit.
- Who is it for?
- Afero suits Go code that reads and writes files in functions you control and wants unit tests without temp directories, plus the composition cases where a jail, a cache or a scratch overlay over a base filesystem is the design. It does not suit anyone needing S3 or MinIO storage out of the box, since the S3, MinIO, Drive and Dropbox backends live in other repositories, and the in-memory backend proves much less than a real disk does.
- 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 15 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 November 14, 2026, and from our analysis. They are not legal advice.
Star history
- spf13/afero6,723 ★ · +19
| Repository | 2026-09-23 | 2026-11-14 |
|---|---|---|
| spf13/afero | 6.7k | 6.7k |
19 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The whole production footprint is accepting an interface and injecting OsFs
Afero's bet is that you change function signatures rather than code. Every function that touches files takes an `afero.Fs` instead of calling `os` directly, and the only wiring step is injecting a backend at the call site. The README puts the production side in two moves: accept `afero.Fs`, then inject `OsFs` in main. Application logic does not change, which is what makes moving to a different backend a single line rather than a refactor.
Two compatibility claims carry that. Afero mirrors the functions of the standard `os` package, and the tree keeps ioutil.go and path.go for the older helper names, so code that called `ioutil.ReadFile` has a counterpart that takes an Fs. It also declares compatibility with the `io/fs` interfaces, and iofs.go is the file that implements it, so an afero.Fs can be handed to code already written against the standard library's filesystem interface.
The design carries a cost too. `afero.Fs` has to be an interface, so every helper call passes the filesystem explicitly, and threading that through constructors, structs and interfaces you do not own is where the adoption cost actually lands.
MemMapFs removes cleanup from tests, and gives up what the OS provides
`MemMapFs` is why most Go developers meet Afero at all: a fast, atomic, concurrent-safe in-memory filesystem, so a test writes its inputs, runs the code and has nothing to clean up afterwards. The example pre-populates it with `afero.WriteFile` and a 0644 mode, calls the same production function with the same path, and checks the returned error. No temp directory, no deferred cleanup.
What it is not is a disk. It holds exactly what was written into it and nothing arrives from outside, so a test that passes against `MemMapFs` has shown your code reads what it wrote, not that it copes with a real directory, a real permission bit, a file another process holds open, or a filesystem that fills up. The library does keep platform differences visible, with const_bsds.go and const_win_unix.go in the tree for constants that differ. Code that depends on those corners still needs one run against `OsFs`.
CopyOnWriteFs, CacheOnReadFs and BasePathFs each wrap one Fs in another
The composition wrappers are the part with the most leverage, because each is one Fs handed to a constructor that returns another Fs. `afero.NewCopyOnWriteFs(base, overlay)` gives a read-only base with a writable layer above it, which is the scratch space for sandboxing: writes land in the overlay and the base is never modified. `afero.NewCacheOnReadFs(base, cache, ttl)` lazily copies files out of a slow base into a fast layer the first time they are read, so a network drive gains an in-memory cache without a prefetch step. `afero.NewBasePathFs(source, path)` restricts every operation to a subdirectory, which the README calls a chroot or a jail.
The other two are filters rather than layers. `afero.NewReadOnlyFs(source)` refuses modification, and `afero.NewRegexpFs(source, regexp)` exposes only paths that match a pattern. They stack in whatever order the problem needs, and because each layer only speaks `afero.Fs`, no layer needs to know what sits underneath it. The composition files sit at the top of the tree as copyOnWriteFs.go, cacheOnReadFs.go, basepath.go, readonlyfs.go and regexpfs.go, most with a test file beside them.
ZipFs and TarFs are read-only, GcsFs and SftpFs are labelled experimental
Remote and archived filesystems are where the maturity labels diverge. The archive backends read only, and the constructor signature is the detail that matters: `zipfs.New(zipReader)` and `tarfs.New(tarReader)` take a reader rather than a path, so they fit a file someone just uploaded. The network backends are `GcsFs` for Google Cloud Storage and `SftpFs`, and both carry an experimental label, which puts them in a different category from the core backends.
One backend exists for serving rather than reading. `afero.NewHttpFs(source)` wraps any Afero filesystem so it can be handed to `http.FileServer`, turning a mounted filesystem into a URL tree without a routing layer of your own.
The tree backs this split up. zipfs/, tarfs/, gcsfs/ and sftpfs/ are separate directories sitting next to the core files, with internal/ holding shared pieces. So even the experimental backends live inside this repository rather than in somebody else's, which means an interface change here lands in the same commit history for all of them, and your upgrade surface stays in one place.
There is no S3 backend here, only links to seven other projects
If your storage is S3 or MinIO, this repository does not implement it. Four third party projects cover the cloud cases: `fclairamb/afero-s3` for S3, described as production-ready and built on the official AWS SDK, `cpyun/afero-minio` for MinIO with S3 compatibility, plus Google Drive and Dropbox backends from `fclairamb/afero-gdrive` and `fclairamb/afero-dropbox`, both with streaming support.
The specialised cases are third party too, and four of them come out of one project called `unmango/aferox`: DockerFs for container filesystems, GitHubFs for repository and releases content, FilterFs for predicate-based filtering and IgnoreFs, which is `.gitignore` aware. Git repository access points at `tobiash/go-gitfs`, and a generic FUSE mount of any Afero backend comes from `JakWai01/sile-fystem`.
That is seven repositories to version alongside this one. An interface change is a version bump for every one of them, none is covered by this library's own test files, and none is marked experimental, which is not the same as maintained. If an S3 bucket is your system of record, then reaching for Afero means adding two dependencies instead of one, and owning both.
Install is one go get, and the module asks for Go 1.26.0
Adding Afero is a module dependency and nothing else:
go get github.com/spf13/aferoimport "github.com/spf13/afero"The module file is short enough to read in full, and it sets two constraints before your own code does anything:
module github.com/spf13/afero
go 1.26.0
require golang.org/x/text v0.42.0The go directive is 1.26.0, so the toolchain requirement is current rather than generous, and there is exactly one dependency, golang.org/x/text v0.42.0.
The newest tag is a year older than the newest commit
The repository is not archived and commits are landing: the last push was on 2026-10-30. The tags tell a different story about releases. v1.15.0 is dated 2025-09-08, and v1.14.0 and v1.13.0 are both dated 2025-03-15, hours apart. Two things follow for a team pinning versions. Roughly a year of commits sits behind the newest tag, and the 1.x line has not moved its minor number since September 2025.
The layout says something about the tooling around that work. Linting is configured in `.golangci.yaml`, appveyor.yml covers Windows CI, and `devenv.nix`, `devenv.yaml` and `devenv.lock` describe a Nix-style development environment, which is a different answer to onboarding than a written setup guide. Tests live beside the code in per-backend files such as memmap_test.go, basepath_test.go, copyOnWriteFs_test.go and regexpfs_test.go rather than in one suite, so a wrapper's behaviour is verified next to the wrapper.
Editorial conclusion
Afero suits Go code that reads and writes files in functions you control and wants unit tests without temp directories, plus the composition cases where a jail, a cache or a scratch overlay over a base filesystem is the design. It does not suit anyone needing S3 or MinIO storage out of the box, since the S3, MinIO, Drive and Dropbox backends live in other repositories, and the in-memory backend proves much less than a real disk does. Before adopting it across a codebase, check the go directive, which is 1.26.0, and pin against v1.15.0 from 2025-09-08 while commits continue past it.
Frequently asked questions
What is Afero used for in Go?
It is a filesystem abstraction that works as a drop-in replacement for the standard os package. You change functions to accept an afero.Fs, inject afero.NewOsFs() in production and afero.NewMemMapFs() in tests, and the same code then runs against disk or entirely in memory.
How do I install Afero in a Go project?
Run go get github.com/spf13/afero and import github.com/spf13/afero. The go.mod in the repository sets go 1.26.0 and has a single dependency, golang.org/x/text v0.42.0.
Which Afero backends are experimental or maintained elsewhere?
GcsFs for Google Cloud Storage and SftpFs are marked experimental in the backend table. S3, MinIO, Google Drive, Dropbox, Git, Docker, GitHub, predicate filtering, .gitignore-aware filtering and FUSE backends all come from separate third party repositories, and the core repository ships none of them.
Official sources
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.
[](https://hysenlabs.com/projects/spf13-afero)Star history chart for your README
[](https://hysenlabs.com/projects/spf13-afero)Was this useful?