Some workloads need a directory that many machines can read and write at the same time: a shared home directory, an application's upload folder behind several web servers, a build cache, model checkpoints written by one job and read by another, or a legacy application that simply expects a POSIX path. Block volumes attach to one instance for read-write use, and object storage has no directories, no locks and no in-place updates. OCI File Storage fills the gap with a managed NFS service: you create a file system, give it a network endpoint, and mount it from as many clients as you like.

The service is simple to start and easy to get subtly wrong: access control sits in three places, throughput is set by a resource most people never think about, and the data protection features have rules that bite during an incident. This article explains each part, builds a working deployment, and covers performance, protection and failure modes.

OCI File Storage: the objects between a client and its bytesApp instancesNFSv3 clients, subnet AOKE podsCSI driver, subnet AOn-prem hostvia FastConnect / VPNTCP 2048-2050, 111Mount targetprivate IP in subnet Bsecurity list / NSG gateExport setexports + option lists/shared/buildsFile system: sharedAD-local, 5-way replicatedFile system: buildssnapshot policy hourlyReplica in region 2read-only until failoverreplication, 15 min+Mount targets carry the network identity and throughput; file systems carry the data.Exports join the two, and their option lists decide who may do what.
The File Storage object model. Clients reach a mount target's private IP; the mount target's export set maps paths to file systems; each export's option list decides access per client address.
Advertisement

The four objects and what each one owns

A file system holds the data. It lives in one availability domain, grows automatically, and you pay for bytes stored rather than a provisioned size. Oracle describes the storage as five-way replicated across fault domains with erasure coding, so a disk or host failure is invisible to you.

A mount target is the NFS endpoint: a highly available service with a private IP in a subnet you choose. It is where network security applies and where throughput is set. One mount target serves up to 100,000 NFS client connections and can serve many file systems.

Every mount target owns one export set holding exports. An export binds a path such as /shared to one file system. A file system can be exported through several mount targets, which is how you add network paths or throughput.

Each export carries an ordered list of export options: per client address range, read-write or read-only, whether root or all users are remapped to an anonymous identity, and whether the client must use a privileged port. This is the NFS-level access control, separate from IAM and VCN rules.

LayerControlsWho usually owns it
IAM policyWho may create, delete, export or snapshot file systems through the APIPlatform team
Security list or NSGWhich client addresses can reach the mount target's ports at allNetwork team
Export optionsWhat a reachable client may do: read, write, act as rootStorage or application owner
POSIX permissionsPer-file and per-directory access by UID and GIDApplication

When debugging, a timeout points at the network layer, a permission error at mount time at export options, and a permission error on a file at POSIX ownership.

The four objects and what each one owns

A file system holds the data. It lives in one availability domain, grows automatically, and you pay for bytes stored rather than a provisioned size. Oracle describes the storage as five-way replicated across fault domains with erasure coding, so a disk or host failure is invisible to you.

A mount target is the NFS endpoint: a highly available service with a private IP in a subnet you choose. It is where network security applies and where throughput is set. One mount target serves up to 100,000 NFS client connections and can serve many file systems.

Every mount target owns one export set holding exports. An export binds a path such as /shared to one file system. A file system can be exported through several mount targets, which is how you add network paths or throughput.

Each export carries an ordered list of export options: per client address range, read-write or read-only, whether root or all users are remapped to an anonymous identity, and whether the client must use a privileged port. This is the NFS-level access control, separate from IAM and VCN rules.

LayerControlsWho usually owns it
IAM policyWho may create, delete, export or snapshot file systems through the APIPlatform team
Security list or NSGWhich client addresses can reach the mount target's ports at allNetwork team
Export optionsWhat a reachable client may do: read, write, act as rootStorage or application owner
POSIX permissionsPer-file and per-directory access by UID and GIDApplication

When debugging, a timeout points at the network layer, a permission error at mount time at export options, and a permission error on a file at POSIX ownership.

Advertisement

The data path and the ports it needs

File Storage speaks NFSv3, with the Network Lock Manager protocol for locks. NFSv3 is not one port: a client asks the portmapper on port 111 where the services live, gets a file handle for the export path from the mount service, sends NFS reads and writes, and uses a separate lock service. On OCI these sit on fixed ports.

# Ingress rules on the mount target's subnet (or, better, an NSG on the mount target)
# stateful, source = the client subnet CIDR, all source ports
TCP  dst 111, 2048, 2049, 2050     # portmapper, mount, NFS, NLM
UDP  dst 111, 2048                 # portmapper and mount over UDP
TCP  dst 2051                      # only if you use in-transit TLS (oci-fss-utils / stunnel)

# Egress rules on the client side mirror these to the mount target's IP or CIDR

The most common first-day failure is a mount that hangs and times out: a missing ingress rule on the mount target side or a missing egress rule on the client side when they sit in different subnets. Put the mount target in a network security group and reference the client NSG as the source, so rules follow resources instead of CIDRs. The OCI networking guide explains how security lists and NSGs combine.

NFSv3 data operations are stateless, so with the hard mount option a client pauses and retries across a mount target failover. Locks are the stateful exception and must be re-established, so test applications that hold long-lived locks against a failover.

Worked example: a shared directory for three kinds of client

Suppose an application tier in 10.0.10.0/24 needs read-write access, a reporting tier in 10.0.40.0/24 must only read, and one admin bastion must fix ownership as root. The Terraform below creates the file system, a mount target with a pinned IP, and an export encoding those three roles.

resource "oci_file_storage_file_system" "shared" {
  compartment_id      = var.compartment_id
  availability_domain = var.ad
  display_name        = "shared"
  kms_key_id          = var.vault_key_id      # omit to use Oracle-managed keys
}

resource "oci_file_storage_mount_target" "mt1" {
  compartment_id      = var.compartment_id
  availability_domain = var.ad                # same AD as the file system
  subnet_id           = var.storage_subnet_id
  ip_address          = "10.0.20.25"          # pin it: clients and fstab will refer to it
  nsg_ids             = [oci_core_network_security_group.fss.id]
  display_name        = "mt1"
}

resource "oci_file_storage_export" "shared" {
  export_set_id  = oci_file_storage_mount_target.mt1.export_set_id
  file_system_id = oci_file_storage_file_system.shared.id
  path           = "/shared"

  # Evaluated top to bottom; the FIRST entry whose source matches the client wins.
  export_options {
    source                         = "10.0.30.10/32"   # the admin bastion
    access                         = "READ_WRITE"
    identity_squash                = "NONE"
    require_privileged_source_port = true
  }
  export_options {
    source                         = "10.0.10.0/24"    # application subnet
    access                         = "READ_WRITE"
    identity_squash                = "ROOT"
    require_privileged_source_port = true
  }
  export_options {
    source                         = "10.0.40.0/24"    # reporting hosts
    access                         = "READ_ONLY"
    identity_squash                = "ALL"
    require_privileged_source_port = true
  }
}

Order matters because File Storage applies the first export option whose source matches the client and ignores the rest. Put the most specific address first. If the application subnet's /24 were listed above the bastion's /32 and the bastion sat inside it, the bastion would silently get root squashed and the admin would see chown fail with no obvious reason.

An export created without options gets the defaults: source 0.0.0.0/0, read-write, no squash, any source port. Only network rules then stand between any routable client and root access to your data, so always replace the default entry. Then mount from each client.

# On an Oracle Linux client: install the NFS client, mount, then persist in /etc/fstab
sudo dnf install -y nfs-utils
sudo mkdir -p /mnt/shared
sudo mount -t nfs -o nfsvers=3,hard,timeo=600,retrans=2 10.0.20.25:/shared /mnt/shared

# /etc/fstab -- _netdev waits for networking, nofail keeps a bad mount from blocking boot
10.0.20.25:/shared  /mnt/shared  nfs  nfsvers=3,hard,timeo=600,retrans=2,_netdev,nofail  0 0

# Confirm what the kernel actually negotiated (rsize, wsize, vers, proto)
nfsstat -m

Use hard mounts for anything that writes: a soft mount returns I/O errors after a timeout and can leave half-written files. nofail matters on cloud instances, where a boot stuck on an unreachable mount is a boot you cannot SSH into.

Identity: UIDs, squashing and the root problem

With the default SYS authentication, NFSv3 trusts the numeric UID and GID the client sends. The server never sees user names. If user app is UID 1001 on one host and UID 1003 on another, they are different owners on the share, and files created on one host appear owned by a stranger on the other. Standardise UIDs and GIDs across every client, through a directory service or configuration management, before data lands.

Squashing defends against clients that lie. ROOT remaps UID 0 to the squash identity, 65534 by default, so a compromised root on an application host cannot rewrite every file. ALL remaps everyone, which suits read-only data. require_privileged_source_port demands a source port below 1024, which only root can open, so an unprivileged process cannot speak NFS with a forged UID.

Export options also list allowed authentication flavours, and File Storage can use LDAP for identity mapping and Kerberos to authenticate users rather than trusting hosts; check current documentation before designing around them.

Performance: the mount target is the throttle

A file system has no provisioned throughput of its own. Bandwidth is a property of the mount target, and the documentation lists four levels: a standard 1 Gbps mount target and high-performance mount targets at 20, 40 and 80 Gbps. You can upgrade a standard mount target in place at any time; the high-performance levels carry a 30-day commitment, are billed in 30-day cycles, and can only be downgraded at the end of a cycle. Per-availability-domain service limits on how many of each level you can have are low by default, so request increases before a project depends on them.

# Upgrade the mount target's throughput level in place (starts a 30-day billing cycle)
oci fs mount-target upgrade-shape --mount-target-id "$MT_OCID" --requested-throughput 20

# Schedule the downgrade for the end of the cycle; it cannot happen sooner
oci fs mount-target schedule-downgrade-shape --mount-target-id "$MT_OCID" --requested-throughput 1

# Snapshots are visible to clients under the hidden .snapshot directory of the export root
ls /mnt/shared/.snapshot/
cp -a /mnt/shared/.snapshot/hourly-2026-10-02/config/app.yaml /mnt/shared/config/app.yaml

You can also export one file system through several mount targets and spread clients across them to multiply aggregate throughput. Neither option helps a single client limited by its own network or NFS concurrency, so benchmark from several clients at once.

Latency is the other half. Every open, stat or readdir is a network round trip, so a build scanning 200,000 files is slow on any network file system. Measure both large sequential and small-file shapes.

# Measure before you tune. Run from several clients at once: one client rarely saturates a mount target.
fio --name=seqread --directory=/mnt/shared/bench --rw=read --bs=1M --size=4G \
    --numjobs=8 --ioengine=libaio --direct=1 --group_reporting

fio --name=smallfiles --directory=/mnt/shared/bench --rw=randwrite --bs=4k --size=64M \
    --numjobs=16 --nrfiles=500 --ioengine=libaio --direct=1 --group_reporting

Each file is allocated at least 8,192 bytes, so fifty million one-kilobyte files are billed as far more than fifty gigabytes.

Snapshots, clones and replication

A snapshot is a point-in-time, copy-on-write view of a whole file system, costing only changed blocks. Clients browse it under the hidden .snapshot directory of the export root, so restoring a deleted file is a copy command, as shown above. Snapshot policies create and expire them on a schedule. Snapshots share the file system's availability domain: they protect against mistakes, not against losing the domain.

A clone turns any snapshot into an independent writable file system immediately. Metadata is copied in the background, which Oracle calls hydration and which can take hours on large file systems. Clones suit test environments built from production data and risky upgrades you may need to throw away.

Replication copies a file system to another availability domain or region on an interval of at least 15 minutes, sending only changes between replication snapshots, so your recovery point is the interval plus transfer time. The target must never have been exported, must have no user snapshots or snapshot policy, and stays read-only while replication runs. To fail over you export it, so plan the client path change in advance.

Encryption and access in transit

Data at rest is always encrypted, with Oracle-managed keys or your own OCI Vault key. Customer-managed keys add revocation and audit, and a new outage mode: disable the key and the data is unreadable.

NFSv3 itself is plaintext. File Storage supports TLS 1.3 in transit through Oracle's oci-fss-utils package or stunnel, over TCP port 2051. The costs: up to 4,096 TLS client connections per mount target instead of 100,000, and client CPU for encryption.

Failure modes seen in practice

  • Mount hangs. Missing security rule on one side, or a route table with no path between the client and the mount target subnet, for example from on-premises. Test with rpcinfo -p 10.0.20.25 before blaming NFS.
  • Permission denied for root. Root squash matched first. Check the order of export options, not just their content.
  • Files owned by nobody or the wrong user. UID drift between clients, or squash ALL applied to a writer.
  • Writes stall at peak. The mount target is saturated; add mount targets or raise its level.
  • Corruption after failover. A soft mount failed mid-write, or the application assumed local lock semantics.
  • Replication cannot be created. The target was once exported or has a snapshot policy.
  • Surprise bill. Snapshots pinning deleted data, a forgotten clone, or a high-performance mount target left upgraded.

Choosing between file, block and object storage

NeedBest fitWhy
Many clients sharing a POSIX directoryFile StorageNFS semantics, locks, no client coordination
One database or boot disk with low latencyBlock VolumeLocal filesystem on an attached device; no network metadata round trips
Data lakes, backups, media at large scaleObject StorageCheapest per byte, HTTP API, lifecycle tiers
Shared files for Windows clients over SMBNot File StorageIt serves NFSv3 only

If you know another cloud's managed NFS, compare Amazon EFS and Azure Files, which differ in where throughput lives and how access is expressed.

What to do next

  1. Deploy the Terraform above in a dedicated storage subnet behind an NSG.
  2. Replace the default export option with ordered, most-specific-first entries using root squash and privileged ports.
  3. Standardise UIDs and GIDs across clients before production data lands.
  4. Mount with nfsvers=3,hard and nofail, and confirm with nfsstat -m.
  5. Run the fio jobs from several clients before choosing a mount target level.
  6. Attach a snapshot policy and practise a file restore and a clone.
  7. For disaster recovery, replicate to a never-exported target in another region and rehearse failover.
  8. Alarm on mount target throughput and budget for storage and snapshot growth.
Key takeaway: OCI File Storage is managed NFSv3 built from four objects: file systems hold the data, mount targets provide the network endpoint and set throughput, exports map paths to file systems, and first-match export options control per-client access. Open the fixed NFS ports in an NSG, replace the permissive default export option with ordered explicit entries, squash root and keep UIDs consistent, and use hard mounts. Scale bandwidth with mount target levels or more mount targets, protect data with snapshots, clones and cross-region replication, and rehearse restores and failover before you need them.