S3 Files
Introduction
Section titled “Introduction”Amazon S3 Files lets you access the objects in a general purpose S3 bucket as a shared file system. Compute services such as Lambda and ECS mount the file system and read and write files with standard file operations, while S3 Files keeps the file system and the bucket in sync in both directions. See the S3 Files documentation for an overview of the service.
LocalStack lets you use the S3 Files APIs in your local environment to create file systems on top of your buckets, manage access points, mount targets, file system policies, and synchronization configurations, and mount file systems into Lambda functions, ECS tasks, and Batch jobs. The supported APIs are available on the API coverage section, which provides information on the extent of S3 Files’ integration with LocalStack.
Getting started
Section titled “Getting started”This guide is designed for users new to S3 Files and assumes basic knowledge of the AWS CLI and our lstk aws command.
Start your LocalStack container using your preferred method. We will demonstrate how to create a versioned bucket, an IAM role for S3 Files, a file system, a mount target, and an access point.
Create a versioned bucket
Section titled “Create a versioned bucket”S3 Files requires a general purpose bucket with versioning enabled, in the same region and account as the file system. Create a bucket:
lstk aws s3api create-bucket --bucket my-s3files-bucket{ "Location": "/my-s3files-bucket", "BucketArn": "arn:aws:s3:::my-s3files-bucket"}Enable versioning on the bucket. The command returns no output:
lstk aws s3api put-bucket-versioning \ --bucket my-s3files-bucket \ --versioning-configuration Status=EnabledCreate an IAM role for S3 Files
Section titled “Create an IAM role for S3 Files”S3 Files assumes an IAM role to read and write the objects in your bucket. Create a trust policy that allows S3 Files to assume the role:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "elasticfilesystem.amazonaws.com" }, "Action": "sts:AssumeRole" } ]}Create the role and attach a policy that grants access to the bucket:
lstk aws iam create-role \ --role-name s3files-role \ --assume-role-policy-document file://trust-policy.jsonlstk aws iam put-role-policy \ --role-name s3files-role \ --policy-name s3files-bucket-access \ --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:ListBucket","s3:ListBucketVersions","s3:GetObject*","s3:PutObject*","s3:DeleteObject*","s3:AbortMultipartUpload"],"Resource":["arn:aws:s3:::my-s3files-bucket","arn:aws:s3:::my-s3files-bucket/*"]}]}'See IAM roles for S3 Files for the full set of permissions AWS recommends for this role.
Create a file system
Section titled “Create a file system”Create a file system on top of the bucket using the CreateFileSystem API:
lstk aws s3files create-file-system \ --bucket arn:aws:s3:::my-s3files-bucket \ --role-arn arn:aws:iam::000000000000:role/s3files-role{ "creationTime": 1790380487.720941, "fileSystemArn": "arn:aws:s3files:us-east-1:000000000000:file-system/fs-c42a80aad0687dc16", "fileSystemId": "fs-c42a80aad0687dc16", "bucket": "arn:aws:s3:::my-s3files-bucket", "prefix": "", "clientToken": "02f9bd54-3ad2-4f07-9907-258628bf7244", "status": "creating", "roleArn": "arn:aws:iam::000000000000:role/s3files-role", "ownerId": "000000000000", "tags": []}The file system starts in the creating status and moves to available once it is ready.
Check its status with the GetFileSystem API:
lstk aws s3files get-file-system --file-system-id fs-c42a80aad0687dc16 --query status"available"If S3 Files cannot assume the role, the file system moves to the error status and its statusMessage explains why, as on AWS.
Create a mount target
Section titled “Create a mount target”A file system can only be mounted through a mount target in the subnet (or availability zone) where your compute runs.
Look up a subnet of the default VPC and create a mount target in it using the CreateMountTarget API:
SUBNET_ID=$(lstk aws ec2 describe-subnets \ --filters Name=default-for-az,Values=true \ --query 'Subnets[0].SubnetId' --output text)
lstk aws s3files create-mount-target \ --file-system-id fs-c42a80aad0687dc16 \ --subnet-id $SUBNET_ID{ "availabilityZoneId": "use1-az6", "ownerId": "000000000000", "mountTargetId": "fsmt-9a5de3f3693f532ba", "fileSystemId": "fs-c42a80aad0687dc16", "subnetId": "subnet-83e9c59fddf26bca4", "ipv4Address": "172.31.9.121", "networkInterfaceId": "eni-53cc3e4f1af1d3f18", "vpcId": "vpc-34e4d3eaed665d31d", "securityGroups": [ "sg-ab01c07b07e57bfae" ], "status": "creating"}Create an access point
Section titled “Create an access point”Access points give an application its own entry point into the file system, with a fixed POSIX identity and root directory.
Create an access point using the CreateAccessPoint API:
lstk aws s3files create-access-point \ --file-system-id fs-c42a80aad0687dc16 \ --posix-user uid=1000,gid=1000 \ --root-directory 'path=/data,creationPermissions={ownerUid=1000,ownerGid=1000,permissions=0755}' \ --tags key=Name,value=my-access-point{ "accessPointArn": "arn:aws:s3files:us-east-1:000000000000:file-system/fs-c42a80aad0687dc16/access-point/fsap-967845c746d9962e9", "accessPointId": "fsap-967845c746d9962e9", "clientToken": "be4fab3c-4279-4fc7-b971-5e44e0b3280f", "fileSystemId": "fs-c42a80aad0687dc16", "status": "available", "ownerId": "000000000000", "posixUser": { "uid": 1000, "gid": 1000 }, "rootDirectory": { "path": "/data", "creationPermissions": { "ownerUid": 1000, "ownerGid": 1000, "permissions": "0755" } }, "tags": [ { "key": "Name", "value": "my-access-point" } ]}Mounting a file system
Section titled “Mounting a file system”In LocalStack, you access an S3 Files file system from compute services: Lambda, ECS, and Batch. LocalStack mounts the file system into the function, task, or job container for you, so you do not need to run any mount command. Mounts work with both the Docker executor and the Kubernetes executor.
To mount a file system, the following requirements apply:
- The file system must have a mount target in the availability zone the compute runs in. Without a mount target, the file system cannot be reached, as on AWS.
- With the Docker executor, LocalStack must have access to the Docker socket, as with any Lambda function or ECS task (
/var/run/docker.sock:/var/run/docker.sock). - With the Kubernetes executor, see the additional Kubernetes requirements.
Lambda
Section titled “Lambda”Lambda functions mount an S3 Files access point through the same FileSystemConfigs field used for EFS.
A function takes a single entry, which is an access point ARN and a LocalMountPath under /mnt.
The function must be connected to a VPC, and every subnet it runs in needs a mount target in its availability zone.
lstk aws lambda create-function \ --function-name my-function \ --runtime python3.13 \ --handler handler.handler \ --role arn:aws:iam::000000000000:role/lambda-role \ --zip-file fileb://function.zip \ --vpc-config SubnetIds=$SUBNET_ID,SecurityGroupIds=sg-ab01c07b07e57bfae \ --file-system-configs Arn=arn:aws:s3files:us-east-1:000000000000:file-system/fs-c42a80aad0687dc16/access-point/fsap-967845c746d9962e9,LocalMountPath=/mnt/dataYour function can then read and write files under /mnt/data.
LocalStack validates the configuration when you create or update the function, in the same order as AWS. It checks that the access point exists in the same region and account, that the file system has a mount target, and that there is a mount target in each availability zone of the function’s subnets.
ECS tasks mount a file system through an s3filesVolumeConfiguration volume in the task definition, referenced from the container’s mountPoints.
See S3FilesVolumeConfiguration for the available fields.
The task definition must include a taskRoleArn.
{ "family": "s3files-task", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512", "taskRoleArn": "arn:aws:iam::000000000000:role/ecs-task-role", "containerDefinitions": [ { "name": "app", "image": "alpine", "command": ["sh", "-c", "echo hello > /mnt/documents/hello.txt"], "mountPoints": [ { "sourceVolume": "documents", "containerPath": "/mnt/documents" } ] } ], "volumes": [ { "name": "documents", "s3filesVolumeConfiguration": { "fileSystemArn": "arn:aws:s3files:us-east-1:000000000000:file-system/fs-c42a80aad0687dc16" } } ]}Register the task definition and run the task in a subnet that has a mount target:
lstk aws ecs register-task-definition --cli-input-json file://task-definition.jsonlstk aws ecs run-task \ --cluster my-cluster \ --task-definition s3files-task \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={subnets=[$SUBNET_ID],assignPublicIp=ENABLED}"You can narrow what a task sees with an accessPointArn, a rootDirectory, or both.
When you set both, the task mounts the rootDirectory path inside the access point’s root directory.
For example, an access point rooted at /team-a combined with "rootDirectory": "/reports" mounts /team-a/reports.
If the file system has no mount target, the task stops with TaskFailedToStart and the same ResourceInitializationError reason AWS reports (Failed to resolve "<az>.<file-system-id>.s3files.<region>.on.aws" ...).
Batch jobs mount a file system through an s3filesVolumeConfiguration volume in the job definition’s containerProperties, referenced from mountPoints.
See S3FilesVolumeConfiguration for the available fields.
The job definition must include a jobRoleArn.
{ "jobDefinitionName": "s3files-job", "type": "container", "platformCapabilities": ["FARGATE"], "containerProperties": { "image": "alpine", "command": ["sh", "-c", "ls /mnt/data"], "resourceRequirements": [ { "type": "VCPU", "value": "0.25" }, { "type": "MEMORY", "value": "512" } ], "executionRoleArn": "arn:aws:iam::000000000000:role/batch-execution-role", "jobRoleArn": "arn:aws:iam::000000000000:role/batch-job-role", "networkConfiguration": { "assignPublicIp": "ENABLED" }, "volumes": [ { "name": "data", "s3filesVolumeConfiguration": { "fileSystemArn": "arn:aws:s3files:us-east-1:000000000000:file-system/fs-c42a80aad0687dc16" } } ], "mountPoints": [ { "sourceVolume": "data", "containerPath": "/mnt/data" } ] }}lstk aws batch register-job-definition --cli-input-json file://job-definition.json{ "jobDefinitionName": "s3files-job", "jobDefinitionArn": "arn:aws:batch:us-east-1:000000000000:job-definition/s3files-job:1", "revision": 1}Batch validates S3 Files volumes with its own rules, which differ from ECS, as on AWS.
The fileSystemArn must be a full ARN (a bare fs- ID is rejected), and Batch stores the volume as given without filling in a default rootDirectory.
Kubernetes
Section titled “Kubernetes”With the Kubernetes executor, LocalStack mounts S3 Files file systems into the pods of Lambda functions, ECS tasks, and Batch jobs using a PersistentVolume and a PersistentVolumeClaim.
The cluster nodes must have NFS client support in their kernel, because the kubelet mounts the file system on the node that runs the pod.
LocalStack’s ServiceAccount also needs permissions that the Helm chart does not grant by default: persistentvolumes at the cluster level, and persistentvolumeclaims and events in LocalStack’s namespace.
You can grant them with the chart’s extraDeploy value:
extraDeploy: - apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: localstack-s3files namespace: "{{ .Release.Namespace }}" rules: - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["events"] verbs: ["get", "list", "watch"] - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: localstack-s3files namespace: "{{ .Release.Namespace }}" roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: localstack-s3files subjects: - kind: ServiceAccount name: '{{ include "localstack.serviceAccountName" . }}' namespace: "{{ .Release.Namespace }}" - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: localstack-s3files rules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: localstack-s3files roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: localstack-s3files subjects: - kind: ServiceAccount name: '{{ include "localstack.serviceAccountName" . }}' namespace: "{{ .Release.Namespace }}"Synchronization between the bucket and the file system
Section titled “Synchronization between the bucket and the file system”S3 Files keeps the file system and the bucket in sync in both directions, and the bucket is the source of truth.
- Bucket to file system: Objects you write to the bucket appear in the file system within seconds. Small objects are imported into the file system according to the synchronization configuration, and larger objects are read directly from the bucket.
- File system to bucket: A file is exported to the bucket as a new object version after it has gone 60 seconds without writes.
A file that is created and deleted within this window never reaches the bucket.
Directories appear in the bucket right away as directory markers (
dir/). See Export window for how the delay behaves.
Each export creates a new version of the object.
S3 Files stores the file’s POSIX attributes (owner, group, permissions, and timestamps) as object metadata, so a chmod or a timestamp change alone exports a new version with updated metadata.
The first export of a file sets the object’s Content-Type from the file extension, and files with an unknown extension get application/octet-stream.
When a file changes in the bucket and in the file system at the same time, the bucket wins.
S3 Files moves your local copy to the .s3files-lost+found-<file-system-id> directory in the root of the file system, which is not exported to the bucket.
S3 Files supports regular files, directories, symbolic links, and FIFOs. As on AWS, hard links, device files, sockets, and writing extended attributes are not supported.
Export window
Section titled “Export window”On AWS, S3 Files exports a file only after it has gone 60 seconds without writes, and this window cannot be configured. Every write restarts the window, so the file is not exported while writes keep arriving. For example, a process that appends to a log file every 5 seconds keeps resetting the window, and the file never reaches the bucket while the process runs.
LocalStack uses the same 60-second window by default.
To speed up manual testing, you can shorten it with the S3FILES_EXPORT_DELAY_SECONDS configuration variable, so that your writes reach the bucket sooner.
Synchronization configuration
Section titled “Synchronization configuration”Each new file system gets a default synchronization configuration, which you can read with the GetSynchronizationConfiguration API:
lstk aws s3files get-synchronization-configuration --file-system-id fs-c42a80aad0687dc16{ "latestVersionNumber": 0, "importDataRules": [ { "prefix": "", "trigger": "ON_DIRECTORY_FIRST_ACCESS", "sizeLessThan": 131072 } ], "expirationDataRules": [ { "daysAfterLastAccess": 30 } ]}With the default import rule, objects smaller than 128 KiB are imported when their directory is first listed.
With the ON_FILE_ACCESS trigger, they are imported when the file is first read.
Data that is not accessed for the number of days in the expiration rule is evicted from the file system and read from the bucket again on the next access.
You can change these rules with the PutSynchronizationConfiguration API, and the changes apply to a running file system right away.
Current Limitations
Section titled “Current Limitations”- List operations do not paginate, and return all results in a single page.
- The account in resource ARNs is not checked. AWS returns an
AccessDeniedExceptionfor ARNs from another account. PutFileSystemPolicyaccepts policies that would lock you out of futurePutFileSystemPolicycalls. AWS rejects them.- The
kmsKeyIdof a file system is stored but not used to encrypt data. - The
networkInterfaceIdof a mount target does not refer to an existing network interface. - IAM authorization for mounts is not enforced, including the
s3files:ClientMountands3files:ClientRootAccesspermissions. - The
secondaryGidsof an access point’s POSIX user are not enforced. - CloudFormation does not add the
aws:cloudformation:*system tags to S3 Files resources. - S3 allows deleting a bucket that still has file systems attached.
API Coverage
Section titled “API Coverage”21 of 21 operations implemented
Available from the Ultimate plan. Licensing details
Find an API
Search the full operation list, then sort the table to compare current support.
Loading operations…
| Verified on Kubernetes | ||
|---|---|---|
| Loading operations… | ||
Complete static API list All 21 operations and their current support status
| Operation | Status |
|---|---|
| CreateAccessPoint | Implemented |
| CreateFileSystem | Implemented |
| CreateMountTarget | Implemented |
| DeleteAccessPoint | Implemented |
| DeleteFileSystem | Implemented |
| DeleteFileSystemPolicy | Implemented |
| DeleteMountTarget | Implemented |
| GetAccessPoint | Implemented |
| GetFileSystem | Implemented |
| GetFileSystemPolicy | Implemented |
| GetMountTarget | Implemented |
| GetSynchronizationConfiguration | Implemented |
| ListAccessPoints | Implemented |
| ListFileSystems | Implemented |
| ListMountTargets | Implemented |
| ListTagsForResource | Implemented |
| PutFileSystemPolicy | Implemented |
| PutSynchronizationConfiguration | Implemented |
| TagResource | Implemented |
| UntagResource | Implemented |
| UpdateMountTarget | Implemented |