TLDR? Dart + Aqueduct (JIT) + Cloud Run = 220ms cold starts, 90ms hot starts. Example repo.

I’m a huge fan of functions-as-a-service. I’ve been using Google Cloud Functions under TypeScript for quite some time, and its totally reasonable pay-as-you-go pricing structure with no servers to worry about is incredible. My only beef is that my cold starts can be real bad… like 10–20 seconds bad, and even with few dependencies. If only we could get faster cold starts… 😏

Since I use Dart quite regularly for building Flutter apps, I had the pleasure of playing around with some server-side frameworks a few months ago to handle both sides of development with one language. My favorite was easily Aqueduct with it’s excellent tooling, Postgres ORM, and support (thanks Joe!). I came to rely on Aqueduct for any Dart server.

Recently, Dart team added full AOT support for server-side which means the future holds 8mb Alpine containers with fast startup times. Unfortunately Aqueduct doesn’t currently support AOT because it makes use of `dart:mirrors`, but this is currently being worked on by the timely and extremely supportive Joe Conway. (Thanks again, Joe!)

Additionally, Google recently announced Cloud Run… a kind of amalgamation of a container host and Cloud Functions. You setup a Docker container that listens for HTTP requests, and it instantiates it in response to requests. My understanding is that Cloud Run will handle 80 concurrent requests per instance, while Cloud Functions will only handle 1 concurrent requests per instance. That means 99% less cold starts, in theory.

So the planets have aligned… Dart supports AOT. Aqueduct is an excellent server-side framework. Google announced Docker based Cloud Run as a FaaS service. I can’t think of any other buzzwords, so lets see how blazing fast this setup can be.

This isn’t a tutorial as much as it’s a short blog, but we’ll go ahead and post up some notes and a repo to refer to.

$ pub global activate aqueduct $ aqueduct create cloud_run_app $ cd cloud_run_app

[rename bin/main.dart to bin/server.dart] [add environment variable support] [add Dockerfile]

$ gcloud auth login $ gcloud config set project myproject-8d802 $ gcloud config set run/region us-central1 $ gcloud builds submit --tag gcr.io/myproject-8d802/helloworld $ gcloud beta run deploy --image gcr.io/myproject-8d802/helloworld --platform managed

The whole thing is pretty straight forward. Setup a Google Cloud project, get the GCP tooling, add your Dockerfile, submit it for a Cloud Build, deploy it to Cloud Run, and smash it with request from Postman to see the results. That’s it!

One critical takeaway is to use google/dart Dockerfile instead of the popular google/dart-runtime because it is more svelte and doesn’t launch Observatory, which added an additional 11 seconds to cold starts.

So how does it perform? Honestly? Extremely well. 220ms cold starts and 90ms hot starts. Real-world responses.

Now, granted, this project is basically hello world, so add in some real work and the response time will get worse. And those are real-world responses from Bangor, ME on a normal cable internet connection hitting us-central1. I don’t want to start a firestorm, but we compared this to a Golang deployment and saw comparable numbers….

So, here’s my takeaway. Dart + Aqueduct + Cloud Run is a toolkit that makes me willing & excited to use Dart for my FaaS projects. Adding AOT support for Aqueduct should improve those start times even more.

Will I end up using it in production? Hard to say, but I’m always happy to have another tool on my belt.