This website uses cookies

Read our Privacy policy and Terms of use for more information.

What it actually is

FSx is AWS running somebody else's file server for you. Each flavor wraps an existing, battle-tested file system: Microsoft's Windows file server (SMB), Lustre (a parallel file system from HPC), NetApp ONTAP (an enterprise NAS with block and file), and OpenZFS (the ZFS lineage, NFS-first). You do not get a "FSx file system". You get one of four very different products, and the first decision is which, because they share almost nothing except the prefix on the API.

What AWS operates: the servers, the disks, the patching, the failover, the automatic backups. What you operate: the VPC placement, the security groups, the Active Directory join (for Windows and optionally ONTAP), the capacity and throughput you provision, and the clients that mount it.

The mental model that saves time: FSx exists so that software written for a specific file system keeps working in AWS without a rewrite. If nothing in your stack demands SMB, Lustre, ONTAP features or ZFS semantics, the answer is usually Amazon EFS or S3, not FSx.

The model

Every FSx type has the same three billing-shaped knobs: storage capacity, throughput capacity, and a deployment type that decides how many Availability Zones your data lives in. The names differ, the shape is the same.

FSx for Windows File Server speaks SMB (2.0 to 3.1.1) and authenticates against Microsoft Active Directory. It has Single-AZ and Multi-AZ deployments (Multi-AZ keeps a standby file server in another AZ and fails over), SSD and HDD storage, and lets you provision storage, SSD IOPS and throughput independently. Backups are automatic, daily and incremental via Volume Shadow Copy.

FSx for Lustre is a scratch pad for compute. Deployment types are Scratch 1 and 2 (no replication, a failed server means lost data), Persistent 1 and Persistent 2 (replicated within one AZ), and an Intelligent-Tiering storage class for Persistent 2 with elastic capacity. Persistent 2 SSD throughput is chosen per TiB: 125, 250, 500 or 1000 MBps per TiB. It exists to feed GPUs and HPC jobs, usually linked to an S3 bucket.

FSx for NetApp ONTAP is multi-protocol: NFS, SMB, iSCSI and NVMe against the same data. It has SSD storage plus an automatic capacity pool tier for cold data, Multi-AZ and Single-AZ deployments, snapshots, clones, SnapMirror replication, FlexCache, SnapLock WORM and deduplication. It is the lift-and-shift target for an existing NetApp estate.

FSx for OpenZFS speaks NFS v3 through v4.2. Deployment types in the API are SINGLE_AZ_1, SINGLE_AZ_2, SINGLE_AZ_HA_1, SINGLE_AZ_HA_2 and MULTI_AZ_1. The docs describe Multi-AZ and Single-AZ HA failover as typically within 60 seconds, and non-HA Single-AZ recovery as typically within 30 minutes. Its signature features are near-instant snapshots, clones, compression (LZ4 or ZSTD), per-user and per-group quotas, and multiple volumes under one file system. Data in cache can serve millions of IOPS at a few hundred microseconds.

One newer thread across the family: AWS added S3 access point permissions to the FSx managed policies in 2025, and OpenZFS volumes can now have S3 access points attached, so the same data can be reached over NFS and over the S3 API.

When to use it, when not to

You have

Use

Why not the neighbor

Windows apps, SMB shares, Active Directory

FSx for Windows File Server

EFS does not speak SMB

Linux NFS, shared by many instances, no sizing wanted

Amazon EFS

FSx OpenZFS makes you pick capacity and throughput

Linux NFS needing snapshots, clones, compression, sub-millisecond latency

FSx for OpenZFS

EFS has no clones and no ZFS-style snapshots

Enterprise NAS, NFS + SMB + iSCSI on one data set, SnapMirror

FSx for NetApp ONTAP

Nothing else on AWS offers multiprotocol with block

ML training or HPC reading hundreds of GBps from S3 data

FSx for Lustre

EFS and OpenZFS are not parallel file systems

Objects, not files

S3

A file system is the wrong shape

The honest rule: pick the protocol your software already speaks first, then pick the type. SMB means Windows or ONTAP. Parallel throughput means Lustre. Everything else on Linux starts with EFS and graduates to OpenZFS when you need snapshots, clones or latency.

What it costs

You pay for provisioned things, not just stored bytes, and that is the surprise for people coming from S3 or EFS. The dimensions: storage capacity (GB-month), throughput capacity (MBps-month), SSD IOPS above the included amount on the types that charge for them, and backup storage (GB-month). ONTAP adds capacity pool storage and capacity pool requests. Lustre Intelligent-Tiering bills for data stored instead of provisioned capacity.

Rates I could confirm on AWS pricing pages for US East (N. Virginia), taken from their worked examples because the rate tables did not render:

  • OpenZFS: SSD storage $0.18 per GB-month Multi-AZ and $0.09 per GB-month Single-AZ 2; throughput $0.87 per MBps-month Multi-AZ and $0.26 per MBps-month Single-AZ 2; backups $0.05 per GB-month. 3 IOPS per GB are included.

  • Windows: HDD storage $0.025 per GB-month and throughput $4.50 per MBps-month in the Multi-AZ HDD example; backups $0.050 per GB-month. I could not confirm SSD rates from the page.

  • Lustre: scratch storage $0.140 per GB-month and persistent storage $0.145 per GB-month in the worked examples; backups $0.050 per GB-month.

  • ONTAP: I could not extract rates. Check the pricing page.

The surprise: throughput is billed whether you push it or not. A tiny OpenZFS file system pays for its minimum throughput every hour it exists. The AWS worked example for a Single-AZ 2 file system (128 GB, 160 MBps, 12 hours in a 30 day month) comes to $0.885, and $0.693 of that is throughput. There is no meaningful free tier I can point to on these pages, so assume you pay from the first minute.

The limits that bite

Windows has the clearest published numbers. Per file system: SSD minimum 32 GiB, HDD minimum 2,000 GiB, maximum 64 TiB for both; SSD IOPS 96 to 400,000; throughput 8 to 12,288 MBps; up to 100,000 file shares; automated backup retention up to 90 days. Per Region defaults: 100 Windows file systems, 10,240 MBps total throughput, 524,288 GiB each of SSD and HDD, 500,000 total SSD IOPS, 500 user-initiated backups. All are adjustable in Service Quotas.

OpenZFS: storage capacity minimum 64 GiB and maximum 524,288 GiB (512 TiB) per the CLI reference. Throughput capacity is a fixed menu: 160, 320, 640, 1280, 2560, 3840, 5120, 7680, 10240 MBps for MULTI_AZ_1 and SINGLE_AZ_2, and 64 to 4096 for SINGLE_AZ_1. The docs say to keep SSD utilization under 80% of provisioned capacity or latency rises. The 64 and 128 MBps tiers cap NFS rsize and wsize below the 1 MB default.

Lustre: capacity comes in steps. Persistent SSD is 1.2 TiB, 2.4 TiB, or multiples of 2.4 TiB. Persistent HDD is multiples of 6.0 TiB or 1.8 TiB depending on the 12 or 40 MBps per TiB option. Scratch and persistent are different failure contracts, which is the limit that matters most: scratch data is gone if a server fails.

The failure modes at scale are mostly operational: a Windows file system that cannot join or reach Active Directory, a security group missing the right ports, and a team that sized throughput once and never revisited it.

Build it: an OpenZFS share, mounted, snapshotted, torn down

I chose OpenZFS because it is the cheapest of the four to try for an hour and it makes the provisioned-throughput bill visible. Honest note up front: the code below follows the AWS CLI reference, API reference and user guide, but I did not run it against an account while writing this. Treat the first run as a test and read the output.

Prerequisites. An AWS account, a VPC with a private subnet, one EC2 Linux instance in the same VPC (Amazon Linux works) with the NFS client installed, and the AWS CLI plus boto3 configured.

IAM. The managed policy AmazonFSxFullAccess covers FSx actions. Creating the first file system in an account also needs permission to create the FSx service-linked role. Attach those to the identity you run the script with.

Estimated cost. Smallest valid OpenZFS Single-AZ 2 file system: 64 GiB at $0.09 per GB-month plus 160 MBps at $0.26 per MBps-month, which works out to about $0.065 per hour at a 730 hour month. One hour of testing is roughly seven cents, plus the EC2 instance. Backups are off in this script.

Step 1: security group. NFS needs inbound TCP and UDP on 111, 2049, and 20001 to 20003. AWS also advises removing outbound rules because OpenZFS file systems do not initiate connections in your VPC.

import boto3

ec2 = boto3.client("ec2")
fsx = boto3.client("fsx")

VPC_ID = "vpc-xxxxxxxx"
SUBNET_ID = "subnet-xxxxxxxx"
CLIENT_CIDR = "10.0.0.0/16"   # your VPC range

sg = ec2.create_security_group(
    GroupName="fsx-openzfs-nfs", Description="NFS to FSx OpenZFS", VpcId=VPC_ID
)["GroupId"]

perms = []
for proto in ("tcp", "udp"):
    for lo, hi in ((111, 111), (2049, 2049), (20001, 20003)):
        perms.append({"IpProtocol": proto, "FromPort": lo, "ToPort": hi,
                      "IpRanges": [{"CidrIp": CLIENT_CIDR}]})
ec2.authorize_security_group_ingress(GroupId=sg, IpPermissions=perms)

Step 2: create the file system. SINGLE_AZ_2 is the Single-AZ deployment type the docs describe as the recommended performance tier for most workloads. The root volume gets LZ4 compression and an NFS export limited to your VPC.

resp = fsx.create_file_system(
    FileSystemType="OPENZFS",
    StorageCapacity=64,
    StorageType="SSD",
    SubnetIds=[SUBNET_ID],
    SecurityGroupIds=[sg],
    OpenZFSConfiguration={
        "DeploymentType": "SINGLE_AZ_2",
        "ThroughputCapacity": 160,
        "AutomaticBackupRetentionDays": 0,
        "RootVolumeConfiguration": {
            "DataCompressionType": "LZ4",
            "NfsExports": [{
                "ClientConfigurations": [
                    {"Clients": CLIENT_CIDR, "Options": ["rw", "crossmnt"]}
                ]
            }],
        },
    },
    Tags=[{"Key": "Name", "Value": "sl123-demo"}],
)
fs = resp["FileSystem"]
fs_id = fs["FileSystemId"]
print(fs_id)

If the API rejects a field, the error names it. Compare against the current create-file-system reference rather than guessing.

Step 3: wait until AVAILABLE.

import time
waiter_state = None
while waiter_state != "AVAILABLE":
    d = fsx.describe_file_systems(FileSystemIds=[fs_id])["FileSystems"][0]
    waiter_state = d["Lifecycle"]
    print(waiter_state)
    if waiter_state == "FAILED":
        raise SystemExit(d.get("FailureDetails"))
    time.sleep(30)

dns = d["DNSName"]
root_volume = d["OpenZFSConfiguration"]["RootVolumeId"]
print(dns, root_volume)

Step 4: mount from the EC2 instance. The path for the root volume is /fsx. The docs recommend rsize and wsize of 1 MB and timeo=600, and nconnect (up to 16, kernel 5.3 or later) for more parallelism.

sudo mkdir -p /fsx
sudo mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,timeo=600,nconnect=16 \
  fs-0123456789abcdef0.fsx.us-east-1.amazonaws.com:/fsx /fsx

Replace the DNS name with the one printed in step 3.

Step 5: verify. Write a file, then confirm the mount and compression behave.

df -h /fsx
echo "hello from fsx" | sudo tee /fsx/hello.txt
sudo dd if=/dev/zero of=/fsx/zeros.bin bs=1M count=512
df -h /fsx

Zeros compress to almost nothing under LZ4, so the used space should barely move on the file system side even though you wrote 512 MB. That is compression doing its job, and a good reminder to benchmark with realistic data before you trust throughput numbers.

Step 6: snapshot, break something, roll back. Snapshots are near-instant and live on the file system.

snap = fsx.create_snapshot(Name="before-oops", VolumeId=root_volume)["Snapshot"]
print(snap["SnapshotId"], snap["Lifecycle"])   # CREATING first

CreateSnapshot returns before it finishes. Poll DescribeSnapshots for Lifecycle AVAILABLE. Then delete hello.txt on the instance, and read it back from the hidden snapshot directory under the mount (the directory is /fsx/.zfs/snapshot/before-oops). If your client does not show it, the user guide section on accessing snapshots has the current instructions.

If you want the other flavors. The same create_file_system call takes FileSystemType="LUSTRE" with LustreConfiguration={"DeploymentType": "PERSISTENT_2", "PerUnitStorageThroughput": 125} and a 1200 GiB capacity, mounted with the lustre client: mount -t lustre -o noatime,flock @tcp:/ /fsx. Lustre security groups must allow TCP 988 and 1021 to 1023 from the group itself or the subnet. Windows needs a managed or self-managed Active Directory first, which is why I did not pick it for a ten minute build.

When it breaks

The mount hangs or times out. Nine times in ten it is the security group: one of the NFS ports (111, 2049, 20001 to 20003, TCP and UDP) is missing, or the instance is in a different VPC with no route. Check the group before you check anything else.

"Access denied" on mount for OpenZFS. The NFS export on the volume did not include your client. The Clients value is an IP, a CIDR or an asterisk; a CIDR that does not contain your instance gives exactly this.

Performance is far below what you expected. Check provisioned throughput first; you are capped at the level you pay for. On the 64 and 128 MBps tiers the maximum rsize and wsize is smaller than 1 MB. Check whether utilization is above 80% of SSD capacity.

Creation ends in FAILED. Read FailureDetails on describe_file_systems. Common causes are a subnet without capacity for the deployment type, a missing service-linked role permission, or a quota.

Windows file system cannot be created or becomes unreachable. Active Directory: DNS resolution from the file system subnets, security group ports to the domain controllers, and credentials. Since late 2025 you can keep the AD credentials in AWS Secrets Manager instead of passing them in the request.

Lustre data vanished. You chose a scratch deployment type, and a server failed. That is the contract.

Cleanup

Unmount first (sudo umount /fsx on the instance). Then delete the file system, which takes its snapshots with it, and then the security group.

fsx.delete_file_system(
    FileSystemId=fs_id,
    OpenZFSConfiguration={"SkipFinalBackup": True},
)
# wait until describe_file_systems no longer lists it, then:
ec2.delete_security_group(GroupId=sg)

Confirm with aws fsx describe-file-systems returning an empty list, and check Billing the next day. Terminate the EC2 instance if you created it for this. Because backups were off and the file system is gone, nothing should keep accruing. A file system that is "just sitting there" still bills for storage and throughput, which is the whole point of this article.

What to take away

FSx is four products. Choose by protocol first: SMB means Windows or ONTAP, parallel throughput means Lustre, NFS with snapshots and clones means OpenZFS, and plain shared Linux storage means EFS. Budget for provisioned throughput, not just bytes. And always run the delete script.

Sources