You know that AWS Lambda is a read-only file system!

You know that AWS Lambda is a read-only file system!

Recently, I encountered an interesting challenge while running an application on AWS Lambda. This application generates and updates numerous files using relative paths. Due to the complexity and the structure of the application, directly uploading files to S3 or modifying the code was not feasible.

Upon deploying the app, I discovered that AWS Lambda does not support writing files to the file system, except for a specific directory: /tmp. This directory acts like a disk mounted on AWS Lambda and can be resized via the Lambda console. Initially, I assumed this might be using Amazon Elastic File System (EFS).

The Solution Attempt

Attempt 1

To work around this limitation, I decided to containerize the application using Docker and configure it to operate within the /tmp directory. Nice enough, it worked perfectly in my local environment. However, when I deployed the containerized application to AWS Lambda, it failed with an error indicating that the entry point /tmp/main did not exist.

Weird, right? I dug deeper and discovered that every time a Lambda function is invoked, an EFS is attached to the Lambda, which corresponds to the /tmp folder. So, whenever Lambda starts, the /tmp folder is empty because it uses a fresh EFS. After the Lambda function exits, this EFS is removed.

Attempt 2

This got tricky. How could I solve this issue? I thought, "Let's copy my application to the /tmp folder after the Lambda function starts." That's exactly what I did. When the Lambda function is invoked, the application is fully copied to the /tmp folder. Then, I internally call the application in the /tmp folder from a mini server. This mini server's sole purpose is to copy the application to the /tmp folder and act as a proxy for the main application.

package main

var sync.Once
func handler(ctx context.Context, event Event) {
        once.Do(func() {
            cmd := exec.Command("cp", []string{"-R", "/app/", "/tmp"}...)

            cmd.Stdout = os.Stdout
            cmd.Stderr = os.Stderr

            if err := cmd.Start(); err != nil {
                fmt.Println(err)
                panic(1)
            }
            if err := cmd.Wait(); err != nil {
                fmt.Println(err)
                panic(1)
            }
        }

        cmd := exec.Command("/tmp/main", ...argsForEvents)
        ...
}

Happy Coding✌🏻

Did you find this article valuable?

Support Dhairya's Blog by becoming a sponsor. Any amount is appreciated!