Quickstart
This guide covers installing ZeroFS, writing a basic configuration, and mounting an S3-backed filesystem. At the end you have a POSIX filesystem that stores its data in S3.
Before you begin, ensure you have access to an S3-compatible storage service and the necessary credentials (access key ID and secret access key). For quick testing without external storage, you can use file:/// URLs to store data locally.
Installation
ZeroFS is distributed as a single binary. The Linux amd64 and arm64 builds are statically linked against musl; the other Linux builds link glibc dynamically. Choose your installation method:
curl -sSfL https://sh.zerofs.net | sh
# Pin a release and install without root
curl -sSfL https://sh.zerofs.net | VERSION=v1.2.5 INSTALL_DIR=$HOME/.local/bin sh
Supported platforms
Each release ships prebuilt binaries for ten targets:
| Target | Platform | Linking |
|---|---|---|
x86_64-unknown-linux-musl | Linux amd64 | Static (musl) |
aarch64-unknown-linux-musl | Linux arm64 | Static (musl) |
armv7-unknown-linux-gnueabihf | Linux armv7 | Dynamic (glibc) |
i686-unknown-linux-gnu | Linux i686 | Dynamic (glibc) |
powerpc64le-unknown-linux-gnu | Linux ppc64le | Dynamic (glibc) |
s390x-unknown-linux-gnu | Linux s390x | Dynamic (glibc) |
riscv64gc-unknown-linux-gnu | Linux riscv64 | Dynamic (glibc) |
x86_64-apple-darwin | macOS x86_64 | Dynamic (system libc) |
aarch64-apple-darwin | macOS aarch64 | Dynamic (system libc) |
x86_64-unknown-freebsd | FreeBSD amd64 | Dynamic (system libc) |
The aarch64 binaries are built with jemalloc configured for page sizes up to 64 KiB (JEMALLOC_SYS_WITH_LG_PAGE=16). They run on kernels with 4 KiB, 16 KiB, or 64 KiB pages.
The prebuilt Linux amd64 binary is compiled with -C target-cpu=x86-64-v3 and requires a CPU with AVX2, BMI2, and FMA: Intel Haswell (2013) or later, AMD Excavator (2015) or later. The amd64 Docker image contains the same binary. On older CPUs the binary exits at startup with an illegal-instruction error (SIGILL). Builds from source target baseline x86-64 and run on those CPUs. See troubleshooting.
Install script options
The install script reads three environment variables. Set them on the right side of the pipe, as in the pinned-release example above.
| Variable | Default | Effect |
|---|---|---|
VERSION | latest | Release tag to install, for example v1.2.5 |
INSTALL_DIR | /usr/local/bin | Destination directory. INSTALL_DIR=$HOME/.local/bin installs without root. The script uses sudo when the destination is not writable. |
SKIP_CHECKSUM | false | true skips SHA-256 verification |
By default the script downloads the multiplatform release tarball and verifies it against the published .sha256 file before extracting.
Verifying downloads
Each release binary and the multiplatform tarball carry GitHub build provenance attestations. Verify a downloaded file with the GitHub CLI:
gh attestation verify zerofs-pgo-multiplatform.tar.gz -R Barre/ZeroFS
The tarball's SHA-256 digest is published alongside it as zerofs-pgo-multiplatform.tar.gz.sha256.
Building with the Web UI
cargo build --release produces a binary without the Web UI; the webui cargo feature is off by default. A binary built without the feature accepts a [servers.webui] section in its configuration but starts no web server and logs no warning.
To build with the Web UI, run make from the repository root:
make build-release
The target builds the frontend first (npm ci, buf generate, the v86 VM image script, npm run build), then runs cargo build --profile release --features webui. Running cargo build --features webui on its own fails: the binary embeds webui/dist at compile time, and the directory exists only after the frontend build. The frontend build needs Node.js (CI uses Node 22), bsdtar (the libarchive-tools package on Debian/Ubuntu), and squashfs-tools. The VM image script downloads an Alpine 3.21 ISO and v86 BIOS files and verifies their SHA-256 checksums.
Prebuilt release binaries and the Docker image are built with the webui feature and include the Web UI.
Docker image
ghcr.io/barre/zerofs is published for linux/amd64, linux/arm64, linux/arm/v7, and linux/386, with a provenance attestation and SBOM. Tags: latest (newest release), X.Y.Z, X.Y, and sha-<commit>.
The entrypoint is the zerofs binary, so arguments after the image name are zerofs subcommands. The container runs as user zerofs (UID 1001), not root; a bind-mounted cache directory must be writable by UID 1001. The image exposes ports 2049 (NFS), 5564 (9P), and 10809 (NBD). Actual listeners come from the [servers.*] sections in the configuration, and each protocol in use needs its own -p mapping.
Configuration
Create a configuration file for ZeroFS:
# Write a config template to ./zerofs.toml
# (overwrites an existing file)
zerofs init
# Or write the template to stdout
zerofs init - > /path/to/zerofs.toml
Edit the configuration file with your settings:
[cache]
dir = "/var/cache/zerofs"
disk_size_gb = 10.0
memory_size_gb = 2.0 # Optional memory cache
[storage]
url = "s3://my-bucket/zerofs-data"
encryption_password = "your-secure-password"
[aws]
access_key_id = "your-access-key"
secret_access_key = "your-secret-key"
region = "us-east-1"
[servers.ninep]
addresses = ["127.0.0.1:5564"]
[servers.nfs]
addresses = ["127.0.0.1:2049"]
Start ZeroFS
Now start ZeroFS with your configuration:
# Start ZeroFS with configuration file
zerofs run --config zerofs.toml
# Or use the shorthand
zerofs run -c zerofs.toml
Mount the Filesystem
With ZeroFS running, mount the filesystem. On Linux, the recommended client is ZeroFS's own zerofs mount. It connects to the 9P server, mounts as a regular user without root, and reconnects on its own if the server restarts or is temporarily unavailable. NFS and the Linux kernel 9P client also work:
# Linux: ZeroFS's own client. Requires [servers.ninep] in your config.
# Create mount point
sudo mkdir -p /mnt/zerofs
# Connect to the 9P server and mount via FUSE (runs as a regular user)
zerofs mount 127.0.0.1:5564 /mnt/zerofs
# Or over a Unix socket for lower-latency local mounts:
# zerofs mount /tmp/zerofs.9p.sock /mnt/zerofs
Using NBD Block Devices
With NBD enabled, block devices are created as files under the .nbd directory. New devices are picked up at runtime without a server restart:
# First, ensure NBD is configured in your TOML:
# [servers.nbd]
# addresses = ["127.0.0.1:10809"]
# Mount the filesystem (if not already mounted)
sudo mount -t nfs -o vers=3,async,nolock 127.0.0.1:/ /mnt/zerofs
# Create a block device file
sudo mkdir -p /mnt/zerofs/.nbd
sudo truncate -s 10G /mnt/zerofs/.nbd/my-device
# Install nbd-client if needed
sudo apt-get install nbd-client # Debian/Ubuntu
sudo yum install nbd # RHEL/CentOS
# Connect to the NBD device
sudo nbd-client 127.0.0.1 10809 /dev/nbd0 -N my-device
# Create filesystem
sudo mkfs.ext4 /dev/nbd0
# Mount the block device
sudo mkdir -p /mnt/nbd0
sudo mount /dev/nbd0 /mnt/nbd0
Verify It's Working
Test your new filesystem:
# Check mount
df -h /mnt/zerofs
# Create a test file
echo "Hello from ZeroFS!" > /mnt/zerofs/test.txt
# Read it back
cat /mnt/zerofs/test.txt
# Check file metadata
ls -la /mnt/zerofs/
Next Steps
The filesystem is mounted and storing its data in S3. From here:
- Configure NBD block devices for raw block storage
- Learn about encryption and how data is encrypted before upload
- Configure caching to size the memory and disk caches
- Explore advanced architectures like geo-distributed storage