Today we’re open-sourcing Supabase Edge Runtime for self-hosting Deno Edge Functions.
Edge Runtime is MIT licensed, written in Rust, and based on the latest Deno Runtime (1.32+). If you’ve been using the Supabase CLI to serve functions then you’re already one of our Beta testers (thanks!).
Host your Edge Functions anywhere
We launched Supabase Edge Functions a little more than a year ago. We use Deno Deploy to host your edge functions globally across 30+ data centers, so your users get super fast responses. This setup works great for us! We didn’t have an easy solution for self-hosting Edge Functions. We’re releasing Edge Runtime to address this.
One of our core principles is “Everything is portable”, meaning you should be able to take any part of the Supabase stack and host it yourself.
Better local development experience
Self-hosting is not the only benefit of Edge Runtime. It will improve the local development experience of Edge Functions.
Serve all functions
Supabase CLI can now serve all local Edge Functions by running
supabase functions serve. Previously, you could only serve a single Edge Function at a time. This was not a great experience for local development. Some of you even devised clever hacks to get around this limitation.
When you run
supabase functions serve, the CLI uses Edge Runtime to serve all functions. It supports JWT verification, import maps, and passing custom environment variables. It hot-reloads local changes, giving you a seamless development experience.
Edge Runtime improves Dev/Prod parity for Edge Functions. You may have encountered issues where an Edge Function works locally but fails when deployed. The main cause for this is Deno Deploy Runtime is more restrictive and only supports a subset of Deno APIs. Edge Runtime exposes the same APIs available in the Deno Deploy Runtime. This will help you spot issues faster while developing and avoid surprises when deploying.
Enforcing memory/duration limits
Another neat feature we built into Edge Runtime is the option to enforce limits on memory and wall-clock durations. Currently, we are setting them to sensible defaults (memory set to 150 MB and execution duration set to 60s). This will allow you to simulate your functions’ resource usage and handle the behavior if they run into the limits. Soon we will allow configuring these limits via CLI config so that you can match them with the real limits of the deployment platform.
How to self-host Edge Functions
We have put together a demo on how to self-host edge functions on Fly.io (you can also use other providers like Digital Ocean or AWS).
To try it yourself:
Clone the demo repository to your machine
Copy your Edge Function into the
./functionsdirectory in the demo repo.
Update the Dockerfile to pull the latest edge-runtime image (check releases)
./functions/main/index.ts, adding any other request preprocessing logic (for example, you can enable JWT validation, handle CORS requests)
fly launchto create a new app to serve your Edge Functions
Access your Edge Function by visiting:
View the logs for the Edge Runtime by visiting Fly.io’s Dashboard > Your App > Metrics. You can serve Edge Runtime from multiple regions by running
fly regions add [REGION].
Standing on the shoulders of Deno
You may wonder why we cannot use Deno Runtime to self-host functions. Isn’t it open-source and available as a Docker container?
Deno Runtime, by default, includes a wide array of built-in APIs, making it easy to use for multiple use cases out of the box. However, this makes it difficult to use for serving web requests. You need the runtime embedded within a web server that can boot fast and, for security, has a more restricted API.
However, Deno’s architecture makes it easy to extend its core capabilities and create a customized runtime to match our needs. Deno provides a Rust crate called
deno_core we can create a JS context (known as a V8 Isolate). A V8 isolate has minimal overhead to boot up and a single process can host multiple V8 isolates. When you load a web page that contains scripts from multiple domains in a browser, each of them runs in a separate v8 isolate.
Edge Runtime implements an HTTP server (using hyper) that listens to incoming requests. When Edge Runtime is booted, it spins up a JS context (V8 isolate), which we call the
Main Worker. Main Worker runs in a separate thread, executing the provided main module. When a new HTTP request is received, the Rust runtime will forward it to the Main Worker.
You can write a main module to handle all incoming requests. This would look like a typical Deno Edge Function. The main difference is that it has access to a global object called “
EdgeRuntime global provides methods to create and access
Main Worker can optionally delegate a request to a UserWorker to handle and respond.
User Workers are separate JS contexts (V8 isolates) that can run a given Edge Function. They have a restricted API (for example, they don’t get access to the host machine’s environment variables). You can also control the memory and duration a User Worker can run.
Here’s a simple implementation of a Main Worker that receives a request, then creates a User Worker and passes the handling of request to the worker.
Open-sourcing Edge Runtime is the first step of an exciting roadmap we have planned for Edge Functions. In the coming months, you will see tighter integrations with the rest of the Supabase ecosystem. Here are some sneak peeks at what is to come next.
API Gateway to other Supabase services
Here’s a simple example of re-routing a request to a different endpoint using Edge Runtime.
Since Edge Runtime’s Main Worker runs in the background as long as the server is running, we can utilize it to run periodic tasks.
For example, here’s a naive implementation of how it can be used to trigger a function every 2 minutes. In production, you need to account for server restarts and timer resetting.
Custom Global Objects
EdgeRuntime without importing a specific module to our function, this was possible because we exposed it as a global object in the runtime.
We can introduce a
Supabase global object that can provide platform specific features. For example, similar to
Deno.writeTextFile , we can expose a
Supabase.writeTextFile which can directly write a file to Supabase Storage.
We 💚 Contributions
We are excited to build Edge Runtime in public and involve the Supabase community in the process. As an initial beta release, there are still bugs and performance quirks to be ironed out. Don’t shy away from trying it though.