I've shipped a few apps on Firebase. It's great, until you want your data on your own server, or a bill that doesn't depend on how many reads your app happened to do this month.
The usual advice is "migrate to Supabase / Appwrite / PocketBase". They're good products. But migrating means rewriting every client call: new SDK, new data model, new auth flow. For an app that's already in production, that's the part that hurts.
So I tried the opposite: keep the Firebase SDKs, replace only the server.
What it is
Aruma is one Go binary plus PostgreSQL, with the dashboard embedded in the same binary. Your app keeps the official Firebase SDK and you swap in the config file Aruma gives you. It's Apache 2.0.
The code in your app doesn't change:
// Plain Firebase JS SDK. Nothing Aruma-specific here.
const auth = getAuth();
await signInWithEmailAndPassword(auth, email, password);
I didn't want emulator mode for this. Emulator mode means adding connect-to-emulator calls to your code, which is exactly the kind of change I was trying to avoid. The SDKs here run in normal production mode.
It's meant to run on a small VPS, so you can host as many projects as the box can take and keep the data in a Postgres you control.
What works today
- Authentication
- Realtime Database
- Security Rules
- Cloud Storage
- Firestore (release candidate, more on that below)
- Projects, apps, API keys, users, rules editor and a data browser in the dashboard
How I tested "compatible"
Firebase SDKs are picky. They notice when a response shape is slightly off, and they fail in confusing ways. So "it seems to work" wasn't good enough.
I compared behavior against real Firebase and against the official emulators, ran the official Firebase test suites against Aruma, and kept test apps for web, Android, iOS, Flutter and the Admin SDK. Where Aruma still behaves differently, it's written down in the docs instead of hidden.
What's not there (read this before you try it)
- Firestore is a release candidate. Some verification is still pending, so I wouldn't call it done.
-
Cloud Functions aren't served. If you use
httpsCallable, the SDK sends the user's ID token to Google, so don't use callable functions with Aruma. - Messaging, App Check and Remote Config are planned, not built.
- Some platforms need a small helper. iOS Storage needs a patched SDK build and Flutter Storage needs one helper call. "Official SDKs, untouched" has this one asterisk.
- Tenant isolation is done in application code, by project number, with a CI check that flags queries missing a project filter. It does not use Postgres row-level security.
- It's pre-1.0.
How I built it
I'm not going to pretend I typed all of it. A lot of the Go was written with Claude Code, and my job was the specs, the compatibility test matrix and reviewing what came back. My first attempt was a fast all-services prototype and it was a bad experience, so I threw that approach away and went one service at a time, with tests before moving on. For some services I also studied existing open-source implementations first instead of designing from scratch.
I think that split is the honest one for a project like this: the AI writes quickly, but the thing that makes it trustworthy is the verification, and that part has to be real.
Try it
git clone https://github.com/arumadev/aruma
cd aruma
make dashboard
make dev
The server prints a one-time setup code in the log. Open localhost:8080, enter it, create your account, then create a project and download the config file. You need Go, PostgreSQL 18 and Node (Node is only for building the dashboard).
What I'd like to know
If you've thought about leaving Firebase, which service would be the one that blocks you? I'm deciding what to harden first and I'd rather hear it from people who have real apps than guess.
Bug reports are very welcome, especially "this SDK call behaves differently from real Firebase". That's the most useful kind of issue for this project.
Top comments (0)