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.

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:

TargetPlatformLinking
x86_64-unknown-linux-muslLinux amd64Static (musl)
aarch64-unknown-linux-muslLinux arm64Static (musl)
armv7-unknown-linux-gnueabihfLinux armv7Dynamic (glibc)
i686-unknown-linux-gnuLinux i686Dynamic (glibc)
powerpc64le-unknown-linux-gnuLinux ppc64leDynamic (glibc)
s390x-unknown-linux-gnuLinux s390xDynamic (glibc)
riscv64gc-unknown-linux-gnuLinux riscv64Dynamic (glibc)
x86_64-apple-darwinmacOS x86_64Dynamic (system libc)
aarch64-apple-darwinmacOS aarch64Dynamic (system libc)
x86_64-unknown-freebsdFreeBSD amd64Dynamic (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.

VariableDefaultEffect
VERSIONlatestRelease tag to install, for example v1.2.5
INSTALL_DIR/usr/local/binDestination directory. INSTALL_DIR=$HOME/.local/bin installs without root. The script uses sudo when the destination is not writable.
SKIP_CHECKSUMfalsetrue 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:

Was this page helpful?