Skip to content
Get Started for Free

S3 Files

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.

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.

S3 Files requires a general purpose bucket with versioning enabled, in the same region and account as the file system. Create a bucket:

Terminal window
lstk aws s3api create-bucket --bucket my-s3files-bucket
Output
{
"Location": "/my-s3files-bucket",
"BucketArn": "arn:aws:s3:::my-s3files-bucket"
}

Enable versioning on the bucket. The command returns no output:

Terminal window
lstk aws s3api put-bucket-versioning \
--bucket my-s3files-bucket \
--versioning-configuration Status=Enabled

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:

trust-policy.json
{
"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:

Terminal window
lstk aws iam create-role \
--role-name s3files-role \
--assume-role-policy-document file://trust-policy.json
lstk 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 on top of the bucket using the CreateFileSystem API:

Terminal window
lstk aws s3files create-file-system \
--bucket arn:aws:s3:::my-s3files-bucket \
--role-arn arn:aws:iam::000000000000:role/s3files-role
Output
{
"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:

Terminal window
lstk aws s3files get-file-system --file-system-id fs-c42a80aad0687dc16 --query status
Output
"available"

If S3 Files cannot assume the role, the file system moves to the error status and its statusMessage explains why, as on AWS.

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:

Terminal window
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
Output
{
"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"
}

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:

Terminal window
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
Output
{
"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"
}
]
}

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 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.

Terminal window
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/data

Your 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.

task-definition.json
{
"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:

Terminal window
lstk aws ecs register-task-definition --cli-input-json file://task-definition.json
lstk 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.

job-definition.json
{
"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" }
]
}
}
Terminal window
lstk aws batch register-job-definition --cli-input-json file://job-definition.json
Output
{
"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.

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:

values.yaml
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.

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.

Each new file system gets a default synchronization configuration, which you can read with the GetSynchronizationConfiguration API:

Terminal window
lstk aws s3files get-synchronization-configuration --file-system-id fs-c42a80aad0687dc16
Output
{
"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.

  • List operations do not paginate, and return all results in a single page.
  • The account in resource ARNs is not checked. AWS returns an AccessDeniedException for ARNs from another account.
  • PutFileSystemPolicy accepts policies that would lock you out of future PutFileSystemPolicy calls. AWS rejects them.
  • The kmsKeyId of a file system is stored but not used to encrypt data.
  • The networkInterfaceId of a mount target does not refer to an existing network interface.
  • IAM authorization for mounts is not enforced, including the s3files:ClientMount and s3files:ClientRootAccess permissions.
  • The secondaryGids of 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.

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
OperationStatus
CreateAccessPointImplemented
CreateFileSystemImplemented
CreateMountTargetImplemented
DeleteAccessPointImplemented
DeleteFileSystemImplemented
DeleteFileSystemPolicyImplemented
DeleteMountTargetImplemented
GetAccessPointImplemented
GetFileSystemImplemented
GetFileSystemPolicyImplemented
GetMountTargetImplemented
GetSynchronizationConfigurationImplemented
ListAccessPointsImplemented
ListFileSystemsImplemented
ListMountTargetsImplemented
ListTagsForResourceImplemented
PutFileSystemPolicyImplemented
PutSynchronizationConfigurationImplemented
TagResourceImplemented
UntagResourceImplemented
UpdateMountTargetImplemented
Was this page helpful?