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✌🏻