Appwrite 2.0: Open Source Firebase Alternative
PROGRAMMING LANGUAGES March 29, 2026, 11:30 a.m.

Appwrite 2.0: Open Source Firebase Alternative

Appwrite 2.0 has emerged as the most compelling open‑source alternative to Firebase, offering a full‑stack backend that you can self‑host or run on any cloud provider. It bundles authentication, databases, storage, functions, and real‑time subscriptions behind a clean REST and GraphQL API, all while keeping your data under your own control. In this article we’ll walk through the core concepts, explore real‑world use cases, and build a couple of end‑to‑end examples that showcase why developers are migrating to Appwrite for production‑grade apps.

What’s New in Appwrite 2.0?

Version 2.0 is a major milestone that introduces a modular architecture, native GraphQL support, and a revamped SDK ecosystem. The new “service‑oriented” design lets you enable or disable individual components (Auth, DB, Functions, etc.) at runtime, reducing your attack surface and resource footprint. Additionally, Appwrite now ships with built‑in rate limiting, audit logs, and a flexible permission model that mirrors Firebase’s rules but with far more granularity.

Another highlight is the Appwrite Console, a modern UI that replaces the older dashboard. It provides real‑time monitoring of function executions, storage usage, and user activity, making it easier for teams to collaborate without digging into raw logs. Finally, the platform now supports multi‑region deployments, enabling you to place services close to your users for sub‑millisecond latency.

Core Building Blocks

Appwrite’s backbone consists of five primary services:

  • Auth – Email/password, OAuth, magic links, and passwordless login.
  • Database – Document‑oriented collections with rich query operators.
  • Storage – Secure file uploads, bucket management, and CDN integration.
  • Functions – Serverless execution in Node.js, Python, Ruby, and Deno.
  • Realtime – Pub/sub channels that push updates to connected clients instantly.

All services share a unified permission model based on roles (owner, member, guest) and scopes (read, write, delete). Permissions are attached at the document or file level, allowing you to enforce fine‑grained access without writing custom middleware.

Architecture Overview

Under the hood, Appwrite runs as a collection of Docker containers orchestrated by Docker Compose or Kubernetes. Each service lives in its own container, communicating over an internal network. This isolation means you can scale the database independently from functions, or replace the default PostgreSQL instance with a managed cloud alternative.

Appwrite’s API gateway sits in front of the services, handling authentication, rate limiting, and request routing. The gateway also injects a JWT for the authenticated user, which downstream services verify before processing the request. This design mirrors the “edge‑to‑origin” flow of Firebase but gives you full visibility into every hop.

Getting Started: One‑Click Deployment

For most developers the fastest way to try Appwrite is the official Docker Compose file. It spins up the gateway, database, storage, and console in under a minute. Run the following commands on a Linux/macOS terminal:

git clone https://github.com/appwrite/appwrite.git
cd appwrite
docker compose up -d

Once the containers are healthy, open http://localhost:5000 in your browser. The console will prompt you to create an admin account; this account owns the first project and can generate API keys for your applications.

After logging in, create a new project called “BlogApp”. The console will provide you with a PROJECT_ID and an API_KEY—both are required by the client SDKs. You can now install the Python SDK (or any other language) and start coding.

Example 1: Authentication & User Management

Let’s build a simple sign‑up and login flow using the Python SDK. This example demonstrates email/password registration, email verification, and JWT retrieval for subsequent API calls.

Setup the SDK

pip install appwrite

Register a New User

from appwrite.client import Client
from appwrite.services.account import Account

client = Client()
client.set_endpoint('http://localhost/v1')   # Your Appwrite endpoint
client.set_project('YOUR_PROJECT_ID')        # Replace with your project ID
client.set_key('YOUR_API_KEY')               # Admin API key

account = Account(client)

# Create a user with email & password
result = account.create(
    user_id='unique()',                     # Auto‑generated ID
    email='alice@example.com',
    password='SuperSecret123',
    name='Alice'
)

print('User created:', result['$id'])

Appwrite automatically sends a verification email (if you’ve configured an SMTP provider). The user must click the link before they can log in.

Login and Retrieve a JWT

# Login with email & password
session = account.create_session(
    email='alice@example.com',
    password='SuperSecret123'
)

# The session object contains a JWT token
jwt = session['secret']
print('JWT token:', jwt)

Store the JWT in a secure HTTP‑only cookie or local storage (if you’re building a SPA). All subsequent SDK calls will automatically include the token, granting the user access to resources they’re permitted to read or write.

Pro tip: Use Appwrite’s session.createOAuth2Session method to enable social logins (Google, GitHub, Apple) with a single line of code. The same JWT flow applies, so you don’t need separate handling for each provider.

Example 2: Real‑Time Database & Serverless Functions

Now we’ll create a “posts” collection, add a document, and listen for real‑time updates in a Python client. We’ll also trigger a serverless function that sends a push notification whenever a new post is created.

Create the Collection via Console

  • Navigate to the Appwrite Console → Database → Collections.
  • Click “Add Collection”, name it posts, and enable permissions: read:any, write:owner.
  • Add fields: title (string), content (string), authorId (string), createdAt (datetime).

Insert a Document from Python

from appwrite.services.databases import Databases
import datetime

db = Databases(client)

doc = db.create_document(
    database_id='default',            # Default DB created on install
    collection_id='posts',
    document_id='unique()',
    data={
        'title': 'Hello Appwrite!',
        'content': 'This is my first post using Appwrite 2.0.',
        'authorId': result['$id'],    # User ID from Example 1
        'createdAt': datetime.datetime.utcnow().isoformat()
    },
    permissions=[
        'read("any")',
        'write("user:' + result['$id'] + '")'
    ]
)

print('Post created with ID:', doc['$id'])

Subscribe to Real‑Time Updates

from appwrite.services.realtime import Realtime

realtime = Realtime(client)

def on_message(event):
    print('Real‑time event:', event['event'])
    print('Payload:', event['payload'])

# Listen to all changes in the posts collection
realtime.subscribe(['databases.default.collections.posts.documents'], on_message)

The callback fires for create, update, and delete events, delivering the full document payload. This is perfect for building live feeds, collaborative editors, or notification dashboards.

Serverless Function: Push Notification

Appwrite Functions run in isolated containers. We’ll write a tiny Node.js function that receives the real‑time event, extracts the post title, and sends a push notification via a third‑party service (e.g., Firebase Cloud Messaging).

// index.js (Node.js runtime)
const fetch = require('node-fetch');

module.exports = async function (req, res) {
    const { event, payload } = req.body; // Appwrite sends event data in the body

    if (event !== 'databases.default.collections.posts.documents.create') {
        return res.send('Ignored non‑create event');
    }

    const title = payload.title;
    const message = `New post: ${title}`;

    // Example: Send to FCM (replace YOUR_SERVER_KEY)
    await fetch('https://fcm.googleapis.com/fcm/send', {
        method: 'POST',
        headers: {
            'Authorization': 'key=YOUR_SERVER_KEY',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            to: '/topics/all',
            notification: { title: 'Appwrite Blog', body: message }
        })
    });

    return res.send('Notification sent');
};

Deploy the function via the console (Functions → Add Function → Runtime: Node.js 18). Then bind the function to the databases.default.collections.posts.documents.create event under “Triggers”. Every time a new post is added, the function fires automatically.

Pro tip: Use environment variables in the function settings to keep API keys out of source code. Appwrite injects them at runtime, and you can rotate them without redeploying.

Example 3: Secure File Uploads with Storage

Many modern apps need to handle user‑generated media—profile pictures, PDFs, or video clips. Appwrite’s Storage service offers bucket‑level permissions, built‑in virus scanning (via ClamAV), and automatic CDN caching.

Create a Bucket

  • Console → Storage → Buckets → Add Bucket.
  • Name it avatars, enable “Public Read” if you want profile images accessible via a public URL.
  • Set permissions: read("any"), write("user:{userId}").

Upload an Image from Python

from appwrite.services.storage import Storage

storage = Storage(client)

with open('profile.jpg', 'rb') as file:
    response = storage.create_file(
        bucket_id='avatars',
        file_id='unique()',
        file=file,
        permissions=[
            'read("any")',
            f'write("user:{result["$id"]}")'   # Owner can replace their avatar
        ]
    )

print('File uploaded. URL:', response['url'])

The returned url is a signed link that expires after a configurable period (default 1 hour). If you enabled public read, you can also construct a permanent URL using the bucket and file IDs.

Serve Files Efficiently

Appwrite automatically integrates with a built‑in CDN (or you can point a custom CDN to the storage endpoint). For high‑traffic apps, configure cache‑control headers in the bucket settings to reduce latency and bandwidth costs.

Pro tip: Combine Appwrite Storage with Functions to generate thumbnails on upload. Trigger a function on the files.create event, resize the image with Sharp (Node.js), and store the thumbnail in a separate bucket.

Real‑World Use Cases

1. SaaS Platforms – Multi‑tenant SaaS products can isolate each tenant in a separate Appwrite project, leveraging the same infrastructure while keeping data siloed. Permissions per tenant are enforced at the collection level, eliminating the need for custom ACL logic.

2. Mobile Games – Real‑time leaderboards, matchmaking queues, and in‑game inventories are built on top of Appwrite’s Realtime and Database services. The low‑latency pub/sub model ensures players see score updates instantly, while Functions handle cheat detection and reward distribution.

3. Content Management Systems – Headless CMS architectures benefit from Appwrite’s flexible schema‑less collections. Editors can upload assets to Storage, publish drafts via Functions, and front‑ends consume data through GraphQL, achieving a fully decoupled workflow.

Scaling & Production Considerations

When moving from a dev environment to production, think about the following:

  1. Horizontal Scaling – Deploy each service behind a load balancer. Appwrite’s stateless gateway makes it easy to add more instances without code changes.
  2. Database Backups – Schedule nightly PostgreSQL dumps or enable point‑in‑time recovery if you use a managed DB service.
  3. Secret Management – Store API keys, JWT secrets, and third‑party credentials in a vault (e.g., HashiCorp Vault) and inject them as environment variables.
  4. Monitoring – Use the built‑in metrics endpoint (/v1/health) and integrate with Prometheus/Grafana for alerting on latency spikes or error rates.

Appwrite also supports “edge functions” that run closer to the user, reducing round‑trip time for latency‑sensitive tasks like image compression or geolocation lookup.

Migration Path from Firebase

Switching from Firebase to Appwrite is smoother than you might think. Most Firebase concepts map 1‑to‑1:

  • Auth → Appwrite Auth (email/password, OAuth, phone).
  • Firestore → Appwrite Database (collections, documents).
  • Cloud Functions → Appwrite Functions (same trigger model).
  • Storage → Appwrite Storage (buckets, file permissions).
  • Realtime → Appwrite Realtime (channel subscriptions).

To migrate data, export your Firebase collections as JSON and import them using the Appwrite SDK’s create_document method. Authentication can be ported by importing user hashes (Appwrite supports bcrypt, Argon2, and scrypt). The open‑source nature of Appwrite also means you can keep legacy data on‑premise while gradually moving new features to the new stack.

Pro tip: When migrating, run both backends in parallel for a short period. Use a feature flag to route new sign‑ups to Appwrite while existing users continue on Firebase. This staged rollout minimizes downtime.

Best Practices for Secure Development

Security is baked into Appwrite, but you still need to follow a few guidelines:

  • Never expose the admin API key on the client. Use server‑side proxies or Functions to perform privileged actions.
  • Enable email verification. Unverified accounts can be limited to read‑only access.
  • Apply least‑privilege permissions. Default to read("any") only for public resources; restrict writes to the owning user or role.
  • Rotate secrets regularly. Appwrite’s console lets you generate new JWT secrets without downtime.
  • Audit logs. Turn on the built‑in audit trail to track changes to collections, permissions, and function deployments.

Conclusion

Appwrite 2.0 delivers a production‑ready, self‑hosted backend

Share this article