# Storage The `storage` construct deploys S3 buckets to store files. ## Quick start ```bash serverless plugin install -n serverless-lift ``` ```yaml service: my-app provider: name: aws constructs: avatars: type: storage plugins: - serverless-lift ``` On `serverless deploy`, a preconfigured S3 bucket will be created. ## How it works The `storage` construct creates and configures the S3 bucket for production: - Files stored in the bucket are automatically encrypted (S3 takes care of encrypting and decrypting data on the fly, without change to our applications). - File versioning is enabled to prevent any accidental data loss. Old versions are automatically purged after 30 days to avoid extra costs. - Storage costs are optimized automatically via [intelligent tiering](https://aws.amazon.com/s3/storage-classes/). To learn more about the architecture of this construct, [read this article](https://medium.com/serverless-transformation/file-storage-on-aws-designing-lift-1caf8c7b9bb0). ## Variables All storage constructs expose the following variables: - `bucketName`: the name of the deployed S3 bucket - `bucketArn`: the ARN of the deployed S3 bucket When [`publicPath`](#public-files) is set, an additional variable is exposed: - `publicUrl`: the base URL of the bucket (e.g. `https://.s3..amazonaws.com`), used to build URLs to public files This can be used to reference the bucket from Lambda functions, for example: ```yaml constructs: avatars: type: storage functions: myFunction: handler: src/index.handler environment: BUCKET_NAME: ${construct:avatars.bucketName} ``` _How it works: the `${construct:avatars.bucketName}` variable will automatically be replaced with a CloudFormation reference to the S3 bucket._ ## Permissions By default, all the Lambda functions deployed in the same `serverless.yml` file **will be allowed to read/write into the bucket** and use common S3 object features such as ACLs, tags, multipart uploads, object attributes, and restores. In the example below, there are no IAM permissions to set up: `myFunction` will be allowed to read and write into the `avatars` bucket. ```yaml constructs: avatars: type: storage functions: myFunction: handler: src/index.handler environment: BUCKET_NAME: ${construct:avatars.bucketName} ``` Automatic permissions can be disabled: [read more about IAM permissions](permissions.md). ## Configuration reference ### Encryption By default, files are encrypted using [the default S3 encryption mechanism](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingServerSideEncryption.html) (free). Alternatively, for example to comply with certain policies, it is possible to [use KMS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html): ```yaml constructs: avatars: # ... encryption: kms ``` ### Lifecycle rules You can configure custom [S3 lifecycle rules](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html) to automatically delete or transition objects: ```yaml constructs: avatars: type: storage lifecycleRules: - prefix: tmp/ expirationInDays: 1 - prefix: cache/ expirationInDays: 7 ``` The configuration maps directly to [CloudFormation LifecycleConfiguration Rules](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-lifecycleconfig-rule.html) with the following conveniences: - `Status: Enabled` is added by default (can be overridden) - Property names can be written in camelCase (e.g. `expirationInDays`) or PascalCase (e.g. `ExpirationInDays`) These rules are added to the default lifecycle rules that Lift adds (intelligent tiering and old version cleanup). ### ACL support Since April 2023, S3 buckets have ACLs disabled by default. However, many tools and libraries (including PHP's [Flysystem](https://github.com/thephpleague/flysystem), used by Laravel) send ACL headers on S3 operations. Without enabling ACLs, these operations will fail. To let the bucket accept ACL headers while keeping the bucket owner in full control: ```yaml constructs: storage: type: storage allowAcl: true ``` This sets the S3 bucket's [Object Ownership](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html) to `BucketOwnerPreferred`. ### CORS To allow browser-based uploads (e.g. via presigned URLs), you can configure CORS on the bucket. **Simple form** — allow a single origin with default methods (`GET`, `PUT`, `DELETE`) and all headers: ```yaml constructs: storage: type: storage cors: "${construct:website.url}" ``` Use `cors: "*"` to allow all origins. **Full form** — define complete CORS rules (property names can be camelCase or PascalCase): ```yaml constructs: storage: type: storage cors: - allowedOrigins: - "${construct:website.url}" allowedMethods: - PUT allowedHeaders: - "*" ``` The full form maps directly to [CloudFormation CorsRules](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-corsconfiguration-corsrule.html). ### Public files By default, the bucket is completely private. To expose **a subset** of the bucket publicly (for example user-uploaded avatars served on a website), use `publicPath`: ```yaml constructs: avatars: type: storage publicPath: public ``` Every object stored under the `public/` prefix immediately becomes **publicly readable** over HTTPS (via a bucket policy), while the rest of the bucket stays private. Files are served directly from S3 at `https://.s3..amazonaws.com/public/...`. Unlike the `assets` of the [`server-side-website`](server-side-website.md) construct, a `storage` bucket is **never emptied or cleared on deploy**, which makes `publicPath` the right place for files generated or uploaded by the application at runtime. To make the **entire bucket** public instead of a single prefix, set `publicPath` to `/` or `*`: ```yaml constructs: avatars: type: storage publicPath: "*" # every object in the bucket is publicly readable ``` ⚠️ With `/` or `*`, _all_ files in the bucket are publicly readable — only do this for buckets that store exclusively public files. Prefer a sub-path (e.g. `public`) when the bucket also holds private files. ## Extensions You can specify an `extensions` property on the storage construct to extend the underlying CloudFormation resources. In the exemple below, the S3 Bucket CloudFormation resource generated by the `avatars` storage construct will be extended with the new `AccessControl: PublicRead` CloudFormation property. ```yaml constructs: avatars: type: storage extensions: bucket: Properties: AccessControl: PublicRead ``` ### Available extensions | Extension key | CloudFormation resource | CloudFormation documentation | |--------------- |------------------------- |------------------------------------------------------------------------------------------------------ | | bucket | AWS::S3::Bucket | [Link](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html) | ### More options Feel like a common extension pattern should be implemented as part of the construct configuration? [Open a GitHub issue](https://github.com/getlift/lift/issues/new).