Migration Guide: NetworkFileSystem
We generally recommend migrating to Volumes. Volumes provide persistent storage across Modal’s compute regions and supports most filesystem operations.
If your application depends on distributed file locking or multiple containers modifying the same file, review the concurrency notes. Depending on your use case you may also want to consider CloudBucketMounts or Sandbox directory snapshots.
Migrating to modal.Volume
Create a new v2 Volume for the migration:
modal volume create my-volume --version=2Copying existing data
Stop all Apps and Sandboxes that write to the NetworkFileSystem before copying. Keep them stopped until you have verified the copy and switched your applications to the replacement.
For a direct copy within Modal, mount the existing NetworkFileSystem and the new,
empty Volume in the same Function. Save this as migrate_nfs.py:
import subprocess
import modal
app = modal.App("nfs-migration")
nfs = modal.NetworkFileSystem.from_name("my-nfs")
volume = modal.Volume.from_name("my-volume")
@app.function(
image=modal.Image.debian_slim().apt_install("rsync"),
network_file_systems={"/nfs": nfs},
volumes={"/volume": volume},
timeout=60 * 60,
)
def copy_data():
subprocess.run(["rsync", "-a", "/nfs/", "/volume/"], check=True)
volume.commit()Run it in the Environment containing your NetworkFileSystem and destination Volume:
modal run --detach migrate_nfs.py::copy_dataWait for the copy and commit to succeed, then verify the destination from a new
container, for example with the modal volume CLI or modal shell --volume my-volume.
Updating application code
Switch from modal.NetworkFileSystem to modal.Volume, and keep the same mount
path so that application paths do not need to change:
# Deprecated usage
nfs = modal.NetworkFileSystem.from_name("my-nfs")
@app.function(network_file_systems={"/data": nfs})
def write_file():
with open("/data/result.txt", "w") as f:
f.write("hello")
# Supported usage
volume = modal.Volume.from_name("my-volume")
@app.function(volumes={"/data": volume})
def write_file():
with open("/data/result.txt", "w") as f:
f.write("hello")
volume.commit()The same needs to be done for @app.cls() and modal.Sandbox.create().
Update scripts that use modal nfs to use modal volume, and the NetworkFileSystem file APIs to use the modal.Volume APIs.
Sharing files between containers
Volume background commits persist changes automatically, but readers in other running containers still need to reload to see them. For a predictable handoff:
- The writer calls
volume.commit()after closing the files it wrote. - A reader that is already running calls
volume.reload()before reading the updated files. Newly started containers mount the latest committed state. - Close all files on the Volume before reloading, and do not access the Volume while a reload is in progress (open files cause reload to fail, and the Volume appears empty to that container during reload).
For example, a reader can fetch a file after the writer has committed it:
@app.function(volumes={"/data": volume})
def read_file():
volume.reload()
with open("/data/result.txt") as f:
return f.read()Inside a Sandbox or shell with a Volume, sync /data commits the
Volume mounted at /data. Wait for the command to succeed before passing the
data to another container.
Volumes support concurrent writers to different files. Concurrent writes to the same file can lose data, and distributed file locking is not supported.
Completing the migration
Redeploy every App that references the NetworkFileSystem. Test a complete write-and-read workflow using the replacement, including reads from a separate container.
Once the migration is verified and no workloads need the original data, you can delete the NetworkFileSystem and its data:
modal nfs delete my-nfsComplete the migration before November 20, 2026.