5 Reasons Why We Moved to Google Cloud Run from Firebase Functions

Jeffrey Rennie
3 min readOct 6, 2024

--

Firebase Functions provide an easy path for programmers to quickly deploy a backend service for their websites. But as the website grows, it grows more difficult to maintain Firebase Functions. In this post, I break down our rationale for moving to Cloud Run into 5 reasons.

1. Firebase Functions lack repeatable deploy and rollback.

Deploying a Firebase Function means deploying JavaScript code, which in our case was transpiled from Typescript. Firebase functions don’t provide a way to archive the transpiled JavaScript code that we deployed.

When rolling back to an earlier version, deploying freshly re-transpiled JavaScript was risky. We had to check out the old source Typescript, recompile and redeploy, and hope that the build environment hadn’t changed since the code was first deployed. Perhaps we upgraded our version of npm or the Typescript compiler in the meantime and the code would fail to compile or behave differently. That’s bad.

A great feature of Firebase is that it allows you to deploy a subset of functions. If you only change the code for one function, you can redeploy just that one function.

A terrible flaw of Firebase is that it allows you to deploy a subset of functions. If you’re running 10 functions, they may have come from 10 different versions of the same code base or different code bases. It becomes very difficult to know exactly which code is running, and therefore which version to roll back to when there’s an issue.

We could build our own system to archive the JavaScript we had deployed, but Google Cloud Run provides a ready-made solution. With Cloud Run, we’re running Docker images archived in Google Artifact Registry. It’s trivial to switch back to an earlier Docker image, and we can control which Docker image is running with Terraform.

2. Firebase Functions lack certainty of runtime environment.

Exactly which version of NodeJs does a Firebase Function run in? What happens when Google decides to stop supporting NodeJs version Q and we’re still running it? What happens when they upgrade from version Q.01 to version Q.02?

We no longer have to worry about the answers to these questions because with Cloud Run, we choose and own the runtime environment inside the Docker image. Google can’t push us to the next version if we don’t want to move to it.

3. With Cloud Run, we can use any programming language.

Firebase Functions can only be written in NodeJs or Python. Cloud Run services can be written in any programming language, and invoke any binary that fits into a Docker image.

Here are some reasons to choose a stack besides NodeJs or Python:

  1. Your entire team are Ruby experts.
  2. Performance is critical, so you need a faster programming language.
  3. You need to manipulate images with ImageMagick.

In practice, all our functions are still running the original NodeJs code, but it’s nice to have these options if we need them.

4. With Cloud Run, there is less for attackers to exploit.

Firebase Functions’ service account has over 8000 privileges by default. Every unnecessary privilege granted to a service account gives an attacker more power if they find an exploit in your service.

Firebase Functions run as the App Engine default service account, and we found no way to change that. We could try removing privileges from that account one by one, but would other parts of our service break? I’m not sure.

Good security practice recommends starting from zero privileges and only adding them as necessary. With Cloud Run, we created a service account with zero privileges and added fewer than 10 privileges it needed to run correctly.

5. Migrating to Cloud Run wasn’t hard.

The Firebase client library invokes Firebase Functions via a simple HTTP protocol that took about 30 minutes to reverse engineer using only a web browser. It wasn’t hard to update the code running in Firebase Functions to run in a Cloud Run Service and speak the same protocol. It was even easier to update the client code to connect to a new host.

If you’d like to see a post about how the Firebase Functions protocol works and how to implement it, please leave a comment below.

--

--

No responses yet