Skip to main content

nfs4fs

nfs4fs is a Linux fsspec implementation backed by VFSI's vectorized NFSv4 client. Bulk fsspec operations are translated into NFS compounds, reducing round trips for workloads with many small files.

Install

python -m pip install nfs4fs

Published wheels use CPython's stable ABI and support standard CPython 3.9 and newer on Linux x86-64 and AArch64. A separate CPython 3.14 free-threaded wheel is built and tested on x86-64. A source build additionally requires Rust, CMake, Clang, Git, Kerberos/GSS development headers, and userspace-RCU development headers. The extension statically links libntirpc and bundles non-platform shared-library dependencies into release wheels.

The package installs fsspec.specs entry points for both nfs4 and vfsi, so normal use does not require import nfs4fs before calling fsspec.

NFSv4

Pass the server as an option or as the URL authority:

import fsspec

fs = fsspec.filesystem(
    "nfs4",
    host="nfs.example",
    root="exports/project",
    auto_mkdir=False,
)

fs.pipe({"/part-0": b"hello", "/part-1": b"world"})
parts = fs.cat(["/part-0", "/part-1"])

with fsspec.open("nfs4://nfs.example/exports/project/part-0", "rb") as file:
    print(file.read())

The client negotiates NFSv4.2 and falls back to NFSv4.1. Set minor_version=1 or minor_version=2 to require a specific version. The current NFS transport uses TCP and AUTH_SYS. Kerberos flavors are not yet exposed by the Python package.

compound_size_limit caps the payload merged into one compound. The default is 1 MiB; lower it when a server has a smaller request limit:

fs = fsspec.filesystem(
    "nfs4", host="nfs.example", compound_size_limit=256 * 1024
)

Connection setup and individual NFS RPCs are independently bounded. Defaults are 10 seconds and 5 seconds, respectively:

fs = fsspec.filesystem(
    "nfs4",
    host="nfs.example",
    connect_timeout=5.0,
    request_timeout=15.0,
)

Operations that vectorize

The largest benefit comes from giving fsspec multiple paths at once:

  • cat, cat_ranges, and open_files batch reads;
  • pipe and put batch writes;
  • recursive get, put, cp, rm, and tree walks use vector metadata and mutation operations;
  • cp_file/copy use NFSv4.2 server-side copy when available, with a client-side fallback.

minor_version(), capabilities(), server_copy_enabled(), compound_stats(), and rpc_stats() are available on the native client for feature inspection and diagnostics.

All data-transfer entry points accept fsspec's callback= argument. Bulk operations (cat, cat_ranges, pipe, get, put, and copy) report item progress on the parent callback and byte progress on a branched callback for each file. Single-file operations report bytes directly, and oversized streaming transfers update after every chunk.

File buffering

Read handles use fsspec's standard per-open caches by default. The default 1 MiB block size matches the VFSI range-I/O path and can be changed per filesystem or per direct fs.open() call:

fs = fsspec.filesystem(
    "nfs4",
    host="nfs.example",
    block_size=256 * 1024,
    cache_type="readahead",
)

with fs.open("/large.parquet", "rb", cache_type="blockcache") as file:
    header = file.read(4096)
    file.seek(-8192, 2)
    footer = file.read()

All cache types registered by fsspec are accepted. Use cache_type="none" for unbuffered reads, especially when another client can modify an already open file. Buffered data belongs to one open handle and is discarded on close. Blocks already fetched by that handle remain cached, but fsspec does not revalidate the source while the handle is open, so uncached blocks may reflect a concurrent writer. This is a per-open cache, not a persistent client cache.

fsspec also provides a persistent sparse-file wrapper under the same blockcache name:

cached = fsspec.filesystem(
    "blockcache",
    target_protocol="nfs4",
    target_options={"host": "nfs.example"},
    cache_storage="/var/tmp/nfs4fs-cache",
    check_files=True,
)

With check_files=True, each open compares the saved source identity with nfs4fs's ukey() and starts a fresh local cache generation when it changes. For NFS this identity uses FATTR4_CHANGE and the file ID; the dummy backend uses file ID, nanosecond timestamps, and size. Validation happens at open, not on every read. With fsspec's default check_files=False, persistent entries are reused without source validation until expiry_time, so that mode is appropriate only when external clients use immutable or versioned paths. Mutations made through the same nfs4fs target invalidate registered persistent block-cache generations automatically, including renamed or recursively deleted subtrees. Already-open handles retain their per-handle cached blocks. cache_check controls how often local cache metadata is reloaded and does not validate the server. invalidate_cache() controls directory listings; use pop_from_cache() to evict persistent file data explicitly.

Files opened together with fsspec.open_files() share a coordinator. A cache miss reads the same aligned range from a bounded group of sibling descriptors through one VFSI vector operation. A requested whole-file read keeps its fast path for the default read-ahead cache; cache_type="blockcache" fills its bounded LRU block-by-block. Speculative siblings are capped to one block (which may contain a whole small file). Set vectorized_buffering=False to disable this adaptive fan-out. The persistent blockcache wrapper participates in the same coordinator: it validates source identities in a metadata batch, serves complete hits locally, and fills sparse misses with vector reads.

Writes remain write-through by default. Set write_buffering=True to delay small writes until flush() or close(); an open_files() write group drains one block from each file per vector operation. Explicit flush() always sends that file's staged data to the server. Update modes containing + retain the unbuffered seekable implementation, and fsspec transactions retain their existing disk-spooled commit behavior.

Directory listing cache

Directory listing caching is disabled by default so that NFS namespaces modified by other clients remain immediately visible. Enable a bounded-staleness cache with the standard fsspec options:

fs = fsspec.filesystem(
    "nfs4",
    host="nfs.example",
    root="exports/project",
    use_listings_cache=True,
    listings_expiry_time=1.0,
    max_paths=1024,
)

listings_expiry_time is the TTL in seconds and max_paths bounds the number of directory paths tracked by fsspec's DirCache; it does not bound the number of entries inside one directory. ls(..., refresh=True) bypasses the cached listing. Native vectorized walks populate the cache for the complete subtree, so repeated walk, find, glob, and du operations can reuse it without turning a miss into one request per directory.

nfs4fs invalidates affected listings after its own writes and namespace mutations, including uncertain failures. Changes made by another client become visible when the TTL expires or when the caller requests a refresh. Use use_listings_cache=False when any period of staleness is unacceptable.

Production notes

  • One filesystem instance owns one native session protected by a mutex. Operations on that instance are serialized. Use separate instances with skip_instance_cache=True when independent connections are required.
  • Native calls release the CPython interpreter lock while waiting for storage. The extension also declares free-threaded CPython support; each filesystem's native-session mutex still serializes that instance.
  • On a transport failure, safe idempotent path reads reconnect and retry once by default. Mutations are never replayed automatically because the server may already have completed an ambiguously failed request. Set auto_reconnect=False to disable automatic read recovery.
  • A process fork is detected before the next operation and creates a fresh native session in the child without sending protocol teardown over the parent's inherited connection. Do not fork while an operation is in flight or share an already-open Nfs4File across a fork. Once application or RPC helper threads exist, use multiprocessing's spawn method. Prefer creating filesystem instances after worker processes start.
  • Call close() or use with fsspec.filesystem(...) as fs: to release the native session deterministically. Closing also removes the instance from fsspec's instance cache.
  • exists, isfile, and isdir return False for missing paths but propagate authentication and connectivity failures. This prevents outages from being mistaken for absent data.
  • auto_mkdir is False by default. Enable it only when write operations are allowed to create missing parents.
  • root rejects . and .. components. Use it to keep all paths under an export-relative prefix; it is not a substitute for server-side authorization.
  • Vector batches are bounded by both batch_size (128 items) and max_batch_bytes (64 MiB). Files larger than that byte threshold stream in transfer_chunk_size chunks (8 MiB). Tune these per server and workload; every value must be positive.
  • Transaction writes use a disk-backed spool after transaction_spool_threshold (8 MiB), upload to unpredictable temporary names in each destination directory, then expose the batch with a vectorized rename. This prevents partial file contents from becoming visible. Like fsspec transactions generally, a multi-file commit is staged rather than a server-wide atomic transaction: a server failure during the final rename batch can expose a prefix of the batch.
  • pipe_file(..., mode="create") and create-mode put use an exclusive server create, so concurrent creators cannot silently overwrite one another.

Local development

On Ubuntu/Debian:

sudo apt-get install clang libclang-dev cmake pkg-config \
  libkrb5-dev libgssglue-dev liburcu-dev
python -m venv .venv
.venv/bin/pip install hypothesis maturin fsspec pytest
cd adapters/nfs4fs
../../.venv/bin/maturin develop
../../.venv/bin/python -m pytest tests

Tests use the local dummy backend unless NFS integration variables are provided. CI runs the same suite against NFSv4.1, NFSv4.2, and patched server-side COPY. Samba coverage lives with the vsmbfs adapter.

The default suite includes a deterministic Hypothesis state machine that runs randomized filesystem histories against both nfs4fs's dummy backend and fsspec's LocalFileSystem oracle. It compares public results, exception classes, directory trees, and file contents after every step. Increase its budget for a longer local fuzz run:

NFS4FS_FUZZ_EXAMPLES=1000 NFS4FS_FUZZ_STEPS=100 \
  ../../.venv/bin/python -m pytest tests/test_fuzz_parity.py

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nfs4fs-0.3.1.tar.gz (224.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

nfs4fs-0.3.1-cp314-cp314t-manylinux_2_28_x86_64.whl (3.3 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.28+ x86-64

nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_x86_64.whl (3.3 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.28+ x86-64

nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_aarch64.whl (3.1 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.28+ ARM64

File details

Details for the file nfs4fs-0.3.1.tar.gz.

File metadata

  • Download URL: nfs4fs-0.3.1.tar.gz
  • Upload date:
  • Size: 224.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nfs4fs-0.3.1.tar.gz
Algorithm Hash digest
SHA256 d7e4306bcfd2d325298e1d897f9f33ea38ddfee51d1a9c7df7dc8dc49fa413d3
MD5 20cdd49cf774ea98f9d53bfc3fbe6400
BLAKE2b-256 5d4c35aabba8065605b3d8a9b59e08fe2b037a9ee10f449433fd9ae265025997

See more details on using hashes here.

Provenance

The following attestation bundles were made for nfs4fs-0.3.1.tar.gz:

Publisher: python-release.yml on vmingchen/vnfs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nfs4fs-0.3.1-cp314-cp314t-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nfs4fs-0.3.1-cp314-cp314t-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8173b67dbbf8f47f5e2f3ef32e5ebc1cedd27f731bdeeaf73eb03004641897b4
MD5 e16724fe505f5c60cba312d4f4e6be33
BLAKE2b-256 a1351ab690ebc297a090f9fe12c6b85ce78bcf1518daf44458313f5fffd471d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for nfs4fs-0.3.1-cp314-cp314t-manylinux_2_28_x86_64.whl:

Publisher: python-release.yml on vmingchen/vnfs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7e34d508982ab1c46f2032704dcddef819188671435f86c287e031834b2419fb
MD5 89cc76789f03f2937b067cb70c001e40
BLAKE2b-256 282f35cf0122e96d1894961c3d8e85ea5fffb1df84f207632b7730d2be55e24d

See more details on using hashes here.

Provenance

The following attestation bundles were made for nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_x86_64.whl:

Publisher: python-release.yml on vmingchen/vnfs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 bc149884738f927d9459de5b42f05f4643f8be95e093e7fcf7811e89e77d81aa
MD5 e163c73c400d069a49c6be050048f604
BLAKE2b-256 002851f373b791adab752bdd664fad4c09d4a432f4a8d9cb15d7b4ac98cef2d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for nfs4fs-0.3.1-cp39-abi3-manylinux_2_28_aarch64.whl:

Publisher: python-release.yml on vmingchen/vnfs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.1 This release

4 files

0.3.0

4 files

0.2.0

4 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page