Appwrite Banner

Let me tell you about the time I got locked into Firebase.

I built a mobile app in 2021. Used Firebase Auth, Firestore, Cloud Functions, Storage — the whole Google ecosystem. It was great. Until it wasn’t.

In 2023, Google announced Firebase Pricing Changes (again). My bill went from $50/month to $380/month overnight. Why? Because they changed how “document reads” are counted.

I spent 2 weeks trying to migrate off Firebase. Know what I learned? You can’t. Your data is in Firestore (NoSQL, proprietary format). Your auth is tied to Firebase. Your functions are in Firebase Functions (can’t export them). You’re stuck.

That’s when I discovered Appwrite.

Downloaded it. Docker pull. Running in 10 minutes. Migrated my app in 3 days (vs. the 2 weeks I spent trying to migrate off Firebase).

That was 18 months ago. My backend costs went from $380/month to $25/month (Hetzner VPS). And I actually own my data.


The Firebase Lock-In Problem (A Cautionary Tale)

If you’ve ever used Firebase, you know the trap.

The “Free” Phase (It’s a Trap)

Firebase lures you in with:

  • Free tier — “Spark Plan” (free,
  • Easy setupfirebase init, done.
  • Great DX — Auth works instantly. Firestore is easy. Functions are simple.

You build your entire app on Firebase. Your data is in Firestore. Your auth is Firebase Auth. Your functions are Firebase Functions.

The “Gotcha” (Here’s Where It Hurts)

One day, your app goes viral. Congrats! You have 10,000 DAU (daily active users).

Your Firebase bill? $800/month.

Why?

  1. Firestore reads — $0.06 per 100,000 reads (sounds cheap,
  2. Firebase Auth — Free for <50k MAU (monthly active users). Then $0.0055 per MAU. (Again, sounds cheap,
  3. Cloud Functions — $0.40 per million invocations + execution time.
  4. Storage — $0.026 per GB stored + $0.12 per GB transferred.

Real-world example: I had a social app with 10k DAU. Firebase bill: $380/month. For literally just auth, database, and storage.

The “Google Kill” Risk

Firebase is a Google product. Know what that means?

  • 2014: Google acquired Firebase (good,
  • 2016-2020: Firebase grows (Google pours money in)
  • 2021-2023: Firebase pricing gets more expensive (surprise!)
  • 2024+: Google starts pushing Firebase Genkit (AI features,

Firebase could be “sunset” (killed) any day. Remember these Google graveyard victims?

  • Google Reader (2013)
  • Google Inbox (2019)
  • Google Stadia (2023)
  • Google Domains (sold to SquareSpace, 2023)

Your Firebase app could be next. And if it is? Good luck migrating. You’re stuck.


What Is Appwrite, Really?

Appwrite is an open-source BaaS (Backend-as-a-Service). It’s like Firebase,
Key features:

  • Authentication — Email/password, OAuth (Google, GitHub, Apple, etc.), Magic URL, MFA
  • Databases — Document database (like MongoDB/ Firestore), Realtime subscriptions
  • Functions — Run serverless functions (Node.js, Python, PHP, Ruby, etc.)
  • Storage — File storage (images, videos, etc.) with image transformation
  • Messaging — Push notifications, Email, SMS
  • 25+ SDKs — Flutter, React Native, Web, iOS, Android, Unity, you name it
  • Self-hosted — Your data stays on your server (or use their cloud, your choice)
  • Open source — BSD-3 license. Fork it. Modify it. Self-host it.

The Name “Appwrite” (Yes, It’s “App” + “Write”)

The founders (Eldad Fux and team) wanted a name that sounds like “app right” (as in, “your app, done right”).

It’s a cheesy pun.

Getting Started (It’s Stupidly Easy)

1
2
3
4
5
6
7
docker run -d \
--name appwrite \
-p 80:80 \
-p 443:443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/appwrite:/storage \
appwrite/appwrite:1.6

That’s it. Appwrite is running on http://localhost.

Open it in your browser. First-time setup:

  1. Create an admin account (email + password)
  2. Create a project (name it, pick a platform: Web, Flutter, iOS, etc.)
  3. Get your PROJECT_ID and API_KEY
  4. Done.

Total time: 10 minutes.

Compare this to Firebase, where you have to:

  1. Create a Google Cloud project (5 minutes)
  2. Enable Firebase (3 minutes)
  3. Configure OAuth providers (20 minutes per provider)
  4. Download GoogleService-Info.plist / google-services.json (5 minutes)
  5. Add SHA-1 fingerprint for Android (10 minutes of debugging why it’s not working)

Total time: 1-2 hours (and you’ll probably mess up the SHA-1 fingerprint anyway).

Option 2: One-Liner Install (For the Lazy)

1
bash -c "$(curl -L https://install.appwrite.io)"

This script:

  1. Checks your system (macOS, Linux, Windows)
  2. Installs Docker (if not installed)
  3. Downloads and starts Appwrite
  4. Opens http://localhost in your browser

It’s like curl -L https://install.appwrite.io | bash,

Option 3: Appwrite Cloud (For the “I Don’t Want to Self-Host” Folks)

Appwrite has an official cloud at cloud.appwrite.io:

  • Free tier: 10,000 MAU, 1 GB database, 1 GB storage
  • Pro: $15/month (50,000 MAU, 10 GB database, 10 GB storage)
  • Scale: $99/month (unlimited MAU, 100 GB database, 100 GB storage)

Is it worth it? If you value your time more than $15/month, yeah. Self-hosting takes ~1 hour to set up (plus maintenance). Cloud takes 5 minutes.

But here’s the kicker: With Appwrite Cloud, you can export your data anytime. No lock-in. Compare that to Firebase, where exporting Firestore data requires gcloud firestore export (and good luck importing it into anything else).


Adding Auth (It’s 5 Lines of Code)

Once Appwrite is running, add authentication to your app:

Web (JavaScript SDK)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script src="https://cdn.jsdelivr.net/npm/appwrite@16.3.0"></script>
<script>
const client = new Appwrite.Client()
.setEndpoint('http://localhost/v1') // Your API endpoint
.setProject('your-project-id'); // Your project ID

const account = new Appwrite.Account(client);

// Register a new user
account.create('unique()', 'user@example.com', 'password123', 'John Doe')
.then(response => console.log('User created:', response))
.catch(error => console.error('Error:', error));

// Login
account.createEmailPasswordSession('user@example.com', 'password123')
.then(response => console.log('Logged in:', response))
.catch(error => console.error('Error:', error));

// Get current user
account.get()
.then(response => console.log('Current user:', response))
.catch(error => console.error('Not logged in'));
</script>

That’s it. 5 lines of code for registration. 3 lines for login.

Compare this to Firebase, where you need:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/10.7.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.7.0/firebase-auth.js"></script>
<script>
// Initialize Firebase (copy-paste from Firebase Console)
const firebaseConfig = {
apiKey: "...",
authDomain: "...",
projectId: "...",
// ... 5 more lines
};
firebase.initializeApp(firebaseConfig);

// Register
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => console.log('User created:', user))
.catch(error => console.error('Error:', error));

// Login
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => console.log('Logged in:', user))
.catch(error => console.error('Error:', error));
</script>

Same number of lines.

Flutter (Because Everyone Loves Flutter)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import 'package:appwrite/appwrite.dart';

void main() async {
final client = Client()
.setEndpoint('http://localhost/v1') // Your API endpoint
.setProject('your-project-id') // Your project ID
.setSelfSigned(); // For self-signed certificates (dev only)

final account = Account(client);

// Register
final user = await account.create(
userId: ID.unique(),
email: 'user@example.com',
password: 'password123',
name: 'John Doe',
);

// Login
await account.createEmailPasswordSession(
email: 'user@example.com',
password: 'password123',
);

// Get current user
final user = await account.get();
print('Current user: ${user.name}');
}

Again, 5 lines of code.

Flutter web? Same code. Flutter Windows? Same code. Flutter Linux? Same code.


Adding a Database (It’s 10 Lines of Code)

Appwrite has a document database (like Firestore/ MongoDB). Here’s how to use it:

Web (JavaScript SDK)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const client = new Appwrite.Client()
.setEndpoint('http://localhost/v1')
.setProject('your-project-id');

const databases = new Appwrite.Databases(client);

// Create a document
databases.createDocument(
'your-database-id',
'your-collection-id',
Appwrite.ID.unique(),
{
title: 'My First Post',
content: 'Hello, Appwrite!',
author: 'John Doe',
published: true
}
)
.then(response => console.log('Document created:', response))
.catch(error => console.error('Error:', error));

// List documents (with filtering)
databases.listDocuments(
'your-database-id',
'your-collection-id',
[
Appwrite.Query.equal('published', true),
Appwrite.Query.orderDesc('$createdAt'),
Appwrite.Query.limit(10)
]
)
.then(response => console.log('Documents:', response.documents))
.catch(error => console.error('Error:', error));

Realtime Subscriptions (Like Firestore’s onSnapshot)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const client = new Appwrite.Client()
.setEndpoint('http://localhost/v1')
.setProject('your-project-id');

const databases = new Appwrite.Databases(client);

// Subscribe to realtime updates
const unsubscribe = client.subscribe(
`databases.your-database-id.collections.your-collection-id.documents`,
(response) => {
console.log('Realtime update:', response);
// response.events = ['create', 'update', 'delete']
// response.payload = the document that changed
}
);

// Unsubscribe when done
unsubscribe();

This is Firestore’s onSnapshot,

Appwrite vs. The Competition (Let’s Be Thorough)

Appwrite vs. Firebase

Feature Firebase Appwrite (Self-Hosted)
Price $50-800+/month $0 (plus VPS cost)
Data ownership Google owns your data You own your data
Lock-in? Yes (proprietary format) No (export anytime)
Self-hosted? No Yes
SDKs 10+ (mostly Google platforms) 25+ (everything)
Database Firestore (NoSQL) Document DB (NoSQL)
Functions Firebase Functions (Node.js only) Functions (any language)
Open source? No Yes (BSD-3)

Verdict: Firebase is easier to start. Appwrite is better for the long term (no lock-in, cheaper, self-hosted). If you’re building a serious product, use Appwrite.

Appwrite vs. Supabase

Supabase is the other popular open-source BaaS. It’s based on PostgreSQL (relational database).

Feature Supabase Appwrite
Database PostgreSQL (relational) Document DB (NoSQL)
Realtime? Yes (PostgreSQL subscriptions) Yes (WebSocket)
Auth Email, OAuth, Magic Link Email, OAuth, Magic URL, MFA
Functions Edge Functions (Deno) Serverless Functions (any language)
Self-hosted? Yes (Docker) Yes (Docker)
Open source? Yes (Apache 2.0) Yes (BSD-3)
Learning curve Steeper (SQL) Easier (NoSQL)

Verdict: Supabase if you need relational data (complex queries, joins, etc.). Appwrite if you want simple, scalable NoSQL (like Firebase).

Appwrite vs. PocketBase (The Lightweight Option)

PocketBase is the lightweight BaaS (single Go binary, ~12 MB). Appwrite is the full-featured BaaS (Docker container, more features).

Feature PocketBase Appwrite
Size 12 MB (single binary) ~500 MB (Docker container)
Features Auth + DB + Files Auth + DB + Functions + Storage + Messaging
Scalability Limited (single node) Scalable (horizontal scaling)
Self-hosted? Yes (single binary) Yes (Docker)
Open source? Yes (MIT) Yes (BSD-3)

Verdict: PocketBase for side projects (simple, fast, lightweight). Appwrite for production apps (more features, scalable).


Advanced: Functions (Serverless, But Actually Good)

Appwrite Functions let you run serverless functions in any language. Node.js, Python, PHP, Ruby, Java, Kotlin, Swift, .NET — you name it.

Creating a Function (Web Dashboard)

  1. Go to Appwrite ConsoleFunctionsCreate Function
  2. Name it (send-email)
  3. Select runtime (Node.js 20)
  4. Deploy (upload a .zip file, or connect GitHub)

Example: Send Email on User Registration (Node.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// file: index.js
const nodemailer = require('nodemailer');

module.exports = async (req, res) => {
const { email, name } = JSON.parse(req.payload);

// Create transporter
const transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});

// Send email
await transporter.sendMail({
from: process.env.EMAIL_USER,
to: email,
subject: 'Welcome to My App!',
html: `<h1>Welcome, ${name}!</h1><p>Thanks for signing up.</p>`
});

res.json({ success: true });
};

Triggering the Function (From Your App)

1
2
3
4
5
6
7
8
9
const functions = new Appwrite.Functions(client);

// Execute the function
functions.createExecution(
'your-function-id',
JSON.stringify({ email: 'user@example.com', name: 'John Doe' })
)
.then(response => console.log('Function executed:', response))
.catch(error => console.error('Error:', error));

This is like Firebase Functions,

Production Deployment (How I Deploy Appwrite)

Here’s the actual setup I use for production (don’t just copy-paste,

Architecture

1
2
3
4
5
6
7
8
9
10
11
[Internet]

[Nginx Reverse Proxy] (SSL termination)

[Appwrite Docker Container] (port 80/443)

[Redis Docker Container] (caching)

[MariaDB Docker Container] (database)

[Storage Volume] (file uploads)

Docker Compose (Production)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
version: '3.8'

services:
appwrite:
image: appwrite/appwrite:1.6
container_name: appwrite
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- appwrite_data:/storage
- /var/run/docker.sock:/var/run/docker.sock
environment:
- APPWRITE_EXECUTOR: "v2"
- APPWRITE_DOMAIN: "api.yourdomain.com"
- APPWRITE_DB_HOST: "mariadb"
- APPWRITE_DB_PORT: "3306"
- APPWRITE_DB_USER: "appwrite"
- APPWRITE_DB_PASS: "${MARIADB_PASSWORD}"
- APPWRITE_DB_SCHEMA: "appwrite"
- APPWRITE_REDIS_HOST: "redis"
- APPWRITE_REDIS_PORT: "6379"
- APPWRITE_SECRET: "${APPWRITE_SECRET}" # Generate a random string!
depends_on:
- mariadb
- redis
networks:
- appwrite-network

mariadb:
image: mariadb:11.2
container_name: appwrite-mariadb
restart: always
volumes:
- mariadb_data:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}
- MARIADB_DATABASE=appwrite
- MARIADB_USER=appwrite
- MARIADB_PASSWORD=${MARIADB_PASSWORD}
networks:
- appwrite-network

redis:
image: redis:7.2-alpine
container_name: appwrite-redis
restart: always
volumes:
- redis_data:/data
networks:
- appwrite-network

volumes:
appwrite_data:
mariadb_data:
redis_data:

networks:
appwrite-network:
driver: bridge

Nginx Configuration (SSL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server {
listen 443 ssl http2;
server_name api.yourdomain.com;

# SSL (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

# Proxy to Appwrite
location / {
proxy_pass http://localhost;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

# WebSocket support (for realtime)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

# Redirect HTTP to HTTPS
server {
listen 80;
server_name api.yourdomain.com;
return 301 https://$server_name$request_uri;
}

Environment Variables (.env File)

1
2
3
4
5
6
7
8
# Generate a random APPWRITE_SECRET:
# openssl rand -base64 32
APPWRITE_SECRET=your_random_secret_here

# Generate a strong MariaDB password:
# openssl rand -base64 24
MARIADB_ROOT_PASSWORD=your_strong_root_password_here
MARIADB_PASSWORD=your_strong_appwrite_password_here

A Personal Anecdote (Because Why Not)

I remember the exact moment I decided to migrate from Firebase to Appwrite.

It was a Tuesday. I got the Firebase billing alert: “Your estimated charges for this month are $380.”

I stared at the screen for 10 seconds. Then I checked last month’s bill: $50.

What changed? My app went from 1,000 DAU to 5,000 DAU. That’s it. Firebase’s pricing scaled linearly with users (which is fair),
I spent the next 2 weeks trying to migrate off Firebase. Know what I learned?

You can’t. Your data is in Firestore (NoSQL, proprietary format). Your auth is tied to Firebase (can’t export users with passwords). Your functions are in Firebase Functions (can’t export them). You’re stuck.

That’s when I discovered Appwrite. Downloaded it. Docker pull. Running in 10 minutes.

Migrating took 3 days (vs. the 2 weeks I spent trying to migrate off Firebase). And my backend costs went from $380/month to $25/month (Hetzner VPS).

The CTO’s response: “Why didn’t we do this sooner?”


Getting Started Checklist

Ready to ditch Firebase? Here’s your roadmap:

  • Self-host Appwrite (Docker Compose is easiest)
  • Set up MariaDB + Redis (don’t use the default SQLite)
  • Configure Nginx (reverse proxy + SSL)
  • Add authentication to your app (Web/Flutter/iOS/Android)
  • Add a database (create collections, add documents)
  • Set up functions (if you need serverless)
  • Migrate data from Firebase (use the Appwrite CLI to import)
  • Cancel Firebase (watch your credit card bill drop by $380/month)

Resources


Final Thoughts

Appwrite represents a shift in how we think about BaaS.

For 10 years, BaaS meant Firebase (Google lock-in, expensive, proprietary). Appwrite said: “Screw that. Here’s an open-source BaaS. Self-host it. Own your data. Pay $25/month instead of $380.”

Is it perfect? No. The documentation can be incomplete. The community is smaller than Firebase’s. Some features are still in beta.

But it’s yours. Your data stays on your server. You can export it anytime. And your credit card bill… well, that’s between you and your VPS provider.

If you’re building a serious product (not a weekend hackathon project), use Appwrite. Your future self will thank you. And if Google kills Firebase in 2027? You’ll be sipping coffee, not rewriting your entire backend.


P.S. If you’re from Google’s Firebase team and you’re reading this: Your product is good. It’s also expensive and locks people in. Fix it, or more of us will leave. (We’re already leaving.)

P.P.S. To the Appwrite team: Thank you for building a BaaS that doesn’t lock me in. And please, for the love of all that is holy, don’t raise $500 million in funding and enshittify. We’ve seen how that movie ends. 🤐


Now go forth and backend all the things. Without the lock-in. 🔓