On this page
How to Deploy Deno to AWS Lambda
AWS Lambda is a serverless computing service provided by Amazon Web Services. It allows you to run code without provisioning or managing servers.
Here's a step by step guide to deploying a Deno app to AWS Lambda using Docker.
The pre-requisites for this are:
Step 1: Create a Deno App Jump to heading
Create a new Deno app using the following code:
Deno.serve((req) => new Response("Hello World from Deno on AWS Lambda!"));
Save this code in a file named main.ts
.
Step 2: Create a Dockerfile Jump to heading
Create a new file named Dockerfile
with the following content:
FROM amazon/aws-lambda-nodejs:20 as lambda-base
# Install Deno
RUN curl -fsSL https://deno.land/install.sh | sh
ENV DENO_INSTALL=/root/.deno
ENV PATH=${DENO_INSTALL}/bin:${PATH}
# Copy function code
WORKDIR ${LAMBDA_TASK_ROOT}
COPY . ${LAMBDA_TASK_ROOT}
# Create wrapper script for Lambda
RUN echo '#!/bin/bash\n\
NODE_OPTIONS="--experimental-fetch" exec node lambda-adapter.js\
' > /var/runtime/bootstrap \
&& chmod +x /var/runtime/bootstrap
# Create Lambda adapter script
RUN echo 'const { spawn } = require("child_process");\n\
const { readFileSync } = require("fs");\n\
\n\
// Parse Lambda runtime API address\n\
const API_ADDRESS = process.env.AWS_LAMBDA_RUNTIME_API;\n\
\n\
// Start Deno server in background\n\
const deno = spawn("deno", ["run", "--allow-net", "--allow-env", "main.ts"]);\n\
deno.stdout.on("data", data => console.log(`stdout: ${data}`));\n\
deno.stderr.on("data", data => console.error(`stderr: ${data}`));\n\
\n\
// Wait for server to start\n\
const sleep = ms => new Promise(r => setTimeout(r, ms));\n\
const waitForServer = async () => {\n\
let attempt = 0;\n\
while (attempt < 10) {\n\
try {\n\
await fetch("http://localhost:8000");\n\
return;\n\
} catch (e) {\n\
attempt++;\n\
await sleep(500);\n\
}\n\
}\n\
throw new Error("Server failed to start");\n\
};\n\
\n\
// Handle Lambda requests\n\
const handleRequest = async () => {\n\
try {\n\
// Get event\n\
const res = await fetch(`http://${API_ADDRESS}/2018-06-01/runtime/invocation/next`);\n\
const requestId = res.headers.get("lambda-runtime-aws-request-id");\n\
const event = await res.json();\n\
\n\
// Forward to Deno server\n\
const denoRes = await fetch("http://localhost:8000");\n\
const body = await denoRes.text();\n\
\n\
// Return response\n\
await fetch(\n\
`http://${API_ADDRESS}/2018-06-01/runtime/invocation/${requestId}/response`,\n\
{\n\
method: "POST",\n\
body: JSON.stringify({\n\
statusCode: denoRes.status,\n\
headers: Object.fromEntries(denoRes.headers.entries()),\n\
body,\n\
isBase64Encoded: false,\n\
}),\n\
}\n\
);\n\
} catch (error) {\n\
console.error("Error:", error);\n\
// Report error\n\
await fetch(\n\
`http://${API_ADDRESS}/2018-06-01/runtime/invocation/${requestId}/error`,\n\
{\n\
method: "POST",\n\
body: JSON.stringify({\n\
errorMessage: error.message,\n\
errorType: error.constructor.name,\n\
stackTrace: error.stack.split("\\n"),\n\
}),\n\
}\n\
);\n\
}\n\
};\n\
\n\
// Main function\n\
const main = async () => {\n\
try {\n\
await waitForServer();\n\
console.log("Server started successfully");\n\
\n\
// Process events in a loop\n\
while (true) {\n\
await handleRequest();\n\
}\n\
} catch (error) {\n\
console.error("Fatal error:", error);\n\
process.exit(1);\n\
}\n\
};\n\
\n\
main();\n\
' > ${LAMBDA_TASK_ROOT}/lambda-adapter.js
EXPOSE 8000
CMD ["node", "lambda-adapter.js"]
This Dockerfile uses the official amazon/aws-lambda-nodejs:20
base image,
which is officially supported by AWS Lambda. We then:
- Install Deno using the official install script
- Set up a custom bootstrap script that Lambda will use to start the function
- Create a Node.js adapter script that:
- Starts your Deno application as a child process
- Acts as a bridge between the AWS Lambda runtime API and your Deno server
- Forwards requests and responses between Lambda and your Deno app
It may seem counterintuitive to use Node.js Lambda base when we're trying to deploy a Deno application. This approach is necessary because of AWS Lambda's Runtime Architecture Lambda's Runtime Interface. AWS Lambda has specific runtime interfaces designed to work with officially supported runtimes (like Node.js, Python, Java, etc.). Deno is not yet an officially supported runtime.
When deploying a container to Lambda, AWS expects it to implement the Lambda Runtime API, which follows specific protocols for receiving events and sending responses. Node.js is Needed as a bridge. The Node.js layer serves as an adapter AWS Lambda's runtime API expectations and your Deno application.
Step 3: Build the Docker Image Jump to heading
Build the Docker image using the following command:
docker build -t deno-lambda .
Step 4: Create an ECR Repository and Push the Image Jump to heading
First, create an ECR repository:
aws ecr create-repository --repository-name deno-lambda --region us-east-1
Note the repository URI in the output, it will look like:
<account_id>.dkr.ecr.us-east-1.amazonaws.com/deno-lambda
Authenticate Docker with ECR:
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <account_id>.dkr.ecr.us-east-1.amazonaws.com
Tag and push your Docker image:
docker tag deno-lambda:latest <account_id>.dkr.ecr.us-east-1.amazonaws.com/deno-lambda:latest
docker push <account_id>.dkr.ecr.us-east-1.amazonaws.com/deno-lambda:latest
Step 5: Create an AWS Lambda Function Jump to heading
- Go to the AWS Lambda console
- Click "Create function"
- Select "Container image"
- Enter a function name (e.g., "deno-function")
- Under "Container image URI", click "Browse images"
- Select the repository and image you just pushed
- Click "Create function"
After the function is created:
- In the "Configuration" tab, click "Edit" in the "General configuration" section
- Set the timeout to at least 30 seconds (to allow for Deno startup time)
- Click "Save"
To create a function URL:
- Go to the "Configuration" tab
- Select "Function URL" from the left sidebar
- Click "Create function URL"
- For "Auth type", select "NONE" (for public access)
- Click "Save"
Step 6: Test the Lambda Function Jump to heading
You can now visit the provided function URL to see your Deno application running on AWS Lambda!
For more advanced setups, you might want to:
- Configure API Gateway to handle more complex HTTP routing
- Set up environment variables for your Deno application
- Create a CI/CD pipeline using GitHub Actions to automate deployments
Troubleshooting Jump to heading
If you encounter issues:
- Cold start timeouts: Increase the Lambda function timeout in the configuration
- Memory issues: Increase the allocated memory for your Lambda function
- Permissions errors: Check the CloudWatch logs for specific error messages
- Request format issues: Modify the lambda-adapter.js script to handle different types of Lambda events
Next Steps Jump to heading
Now that you've successfully deployed a simple Deno application to AWS Lambda, consider exploring:
- AWS SAM for more complex serverless deployments
- API Gateway integration
- AWS CDK for infrastructure as code
🦕 You've successfully deployed a Deno application to AWS Lambda!