How to Migrate from Firebase to Appwrite 2026
How to Migrate from Firebase to Appwrite
Appwrite is a self-hosted Firebase alternative with a similar developer experience — databases, auth, storage, functions, and real-time, all in one Docker deployment. Here's how to migrate.
What Maps
| Firebase | Appwrite | Effort |
|---|---|---|
| Firestore | Appwrite Databases | 🟡 Medium — document-based, easier than SQL migration |
| Firebase Auth | Appwrite Auth | 🟢 Low — similar API |
| Cloud Storage | Appwrite Storage | 🟢 Low — file migration |
| Cloud Functions | Appwrite Functions | 🟡 Medium — rewrite to Appwrite SDK |
| Realtime Database | Appwrite Realtime | 🟢 Low — subscription-based |
| Firebase Hosting | Not included | Use Vercel/Netlify |
Key advantage: Appwrite uses a document-based database, so the Firestore migration is easier than going to PostgreSQL.
Step 1: Deploy Appwrite
docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:latest
Or with Docker Compose for production:
docker compose up -d
Access the console at localhost:80 and create your project.
Step 2: Migrate Database
Appwrite uses a document database — collections with documents, similar to Firestore.
Export Firestore:
const admin = require('firebase-admin');
admin.initializeApp();
async function exportCollection(name) {
const snapshot = await admin.firestore().collection(name).get();
return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
}
Create Appwrite collections and import:
const { Client, Databases, ID } = require('node-appwrite');
const client = new Client()
.setEndpoint('https://your-appwrite.com/v1')
.setProject('your-project')
.setKey('your-api-key');
const databases = new Databases(client);
// Create collection
await databases.createCollection('main-db', 'posts', 'Posts');
// Create attributes
await databases.createStringAttribute('main-db', 'posts', 'title', 255, true);
await databases.createStringAttribute('main-db', 'posts', 'content', 10000, false);
// Import documents
for (const post of firestorePosts) {
await databases.createDocument('main-db', 'posts', ID.unique(), {
title: post.title,
content: post.content,
});
}
Step 3: Migrate Auth
const { Users } = require('node-appwrite');
const users = new Users(client);
// Import users from Firebase export
for (const user of firebaseUsers) {
await users.create(
ID.unique(),
user.email,
undefined, // phone
undefined, // password (users will reset)
user.displayName
);
}
Step 4: Migrate Storage
const { Storage } = require('node-appwrite');
const storage = new Storage(client);
// Create bucket
await storage.createBucket('uploads', 'Uploads');
// Upload files
for (const file of firebaseFiles) {
const content = await downloadFromFirebase(file.path);
await storage.createFile('uploads', ID.unique(), content);
}
Step 5: Update Client Code
Before (Firebase):
import { getFirestore, collection, getDocs } from 'firebase/firestore';
const db = getFirestore();
const snapshot = await getDocs(collection(db, 'posts'));
After (Appwrite):
import { Client, Databases } from 'appwrite';
const client = new Client()
.setEndpoint('https://your-appwrite.com/v1')
.setProject('your-project');
const databases = new Databases(client);
const response = await databases.listDocuments('main-db', 'posts');
Step 6: Set Up Realtime
// Appwrite realtime subscriptions
import { Client } from 'appwrite';
const client = new Client().setEndpoint('...').setProject('...');
client.subscribe('databases.main-db.collections.posts.documents', (response) => {
console.log('Document changed:', response.payload);
});
The Bottom Line
Appwrite's document-based database makes Firebase migration easier than going to a relational database like Supabase. The API patterns are similar, and the self-hosted deployment gives you full control. Plan for 1-2 weeks for a medium-sized app.
Why Teams Migrate Away from Firebase in 2026
Firebase is an excellent product for early-stage development. The free Spark plan genuinely covers most small apps, and the developer experience is genuinely good — especially for mobile teams. The problem emerges at scale.
Firebase's Blaze pricing charges per operation: Firestore reads at $0.06 per 100,000, writes at $0.18 per 100,000, storage at $0.05/GB/month, and bandwidth at $0.12/GB. These costs are predictable and manageable at small scale, but they compound unexpectedly as apps grow. A mobile app with 50,000 daily active users doing moderate Firestore activity can easily hit $300–600/month before you notice the bill climbing.
Beyond cost, vendor lock-in is a real concern with Firebase. Firestore's data model, security rules, and SDK patterns are proprietary. If you ever want to move off Firebase, every layer of your application needs to change. Appwrite gives you the same general development experience — document model, real-time subscriptions, server-side functions, auth — but running on your infrastructure with portable data.
For teams evaluating multiple self-hosted BaaS options, the Supabase vs Appwrite comparison covers both platforms in depth. Supabase is the better choice if your data is relational and you want full SQL access; Appwrite is better if you prefer the document model or need serverless functions in languages beyond TypeScript. You can also compare Appwrite against PocketBase, which is an even lighter-weight single-binary option for smaller apps. For a broader overview of open source Firebase alternatives, OSSAlt covers all the major options.
Firebase's pricing trajectory has become increasingly difficult to predict for applications that experience sudden growth. When a mobile app or web application goes viral, Firebase Firestore read and write costs can spike by orders of magnitude within hours. Teams have reported unexpected Firebase bills of $2,000–10,000 for brief traffic spikes on apps that normally cost $30/month. Self-hosted Appwrite eliminates this billing cliff entirely — one VPS handles whatever traffic your application serves without per-operation charges.
Setting Up Appwrite for Production
Before migrating production data, deploying Appwrite correctly for production use is worth getting right from the beginning. The quick-start installation above works for development, but a production Appwrite deployment has additional considerations.
Storage configuration matters immediately. Appwrite stores uploaded files on disk by default, using the Docker volume mount. For production, configure S3-compatible external storage (MinIO, SeaweedFS, or AWS S3) so that file uploads survive container restarts and VPS migrations without manual file copying. This is configured in the Appwrite console under Settings → Storage before you begin storing production data.
Database backup is the other critical production consideration. Appwrite uses MariaDB internally, and MariaDB backups require mysqldump running against the Appwrite MariaDB container. Automate a daily mysqldump run that saves the output to your backup storage. A weekly test restoration to a development environment verifies the backup is recoverable before you need it in an emergency.
SMTP configuration for authentication emails needs to work correctly from day one. Appwrite sends email verification messages, password reset emails, and magic link authentication emails. Use a transactional SMTP relay (Amazon SES, Postmark, or Resend) rather than a self-hosted mail server to ensure reliable email delivery. Configure this in the Appwrite console before sending authentication emails to real users.
The infrastructure team at a mid-size startup recently documented their Firebase-to-Appwrite migration publicly: their primary motivation was cost predictability, not feature parity. Firebase's feature set was adequate for their use case. The migration took approximately two weeks of engineering time, and their monthly infrastructure cost dropped from $400–800 (variable) to $35 (fixed). The predictability, they noted, was actually more valuable than the cost reduction itself — it removed a category of financial risk from their operating budget.
Data sovereignty is the other migration driver that often goes undiscussed. Firebase runs on Google Cloud infrastructure in Google's chosen regions. Organizations subject to data residency requirements (EU data protection laws, Canadian PIPEDA, Australian Privacy Act) may have limited control over where Firebase stores their data. Running Appwrite on a VPS in your required jurisdiction puts you in full control of data residency, and there is no third-party cloud provider whose policies you need to monitor for changes.
Common Pitfalls in Firebase to Appwrite Migrations
The migration steps above cover the mechanical process, but there are several places where teams commonly run into trouble.
Firestore security rules don't translate directly. Firebase's security rules are a custom expression language that evaluates read and write permissions at the database level. Appwrite uses collection-level permissions and document-level permissions, but the model is different. You will need to re-implement your security logic in Appwrite's permission system rather than trying to translate rules one-to-one. Budget time for this — it is often the most time-consuming part of the migration.
User password hashes are not exportable. Firebase uses bcrypt with a proprietary cost parameter for password storage. When you export users, you get metadata but not their passwords. This means all migrated users will need to reset their passwords after the migration. Plan your user communication strategy in advance — a well-written migration email that explains the password reset process prevents support tickets.
Real-time subscription semantics differ. Firebase's onSnapshot listens to a document or collection and fires on every change. Appwrite's real-time uses an event-based subscription model with channel strings (like databases.main-db.collections.posts.documents). The semantics are similar but not identical — review all your real-time subscription code and test edge cases, especially around connection loss and reconnection behavior.
Cloud Function triggers need review. Firebase Cloud Functions can trigger on Firestore document writes, auth events, storage uploads, and more. Appwrite Functions can trigger on events (Appwrite calls them "events" in the console), but the set of available triggers and their payload formats differ. Map each Firebase function trigger to its Appwrite equivalent before starting the migration.
Test thoroughly in staging. Run the complete migration on a copy of your production data in a staging environment before touching production. Validate user logins, test every API endpoint, and run through your application's critical user journeys end-to-end. Firebase migrations that looked complete on paper have surprised teams with subtle data format differences that only surface during real usage.
Maintaining Appwrite Long-Term
Self-hosted Appwrite requires ongoing maintenance to stay secure and up-to-date. Appwrite releases new versions regularly, and each release typically includes bug fixes, security patches, and new features. The upgrade process involves pulling updated Docker images and running database migrations if the release includes schema changes.
Before each upgrade, review the release notes on GitHub for migration instructions. Some releases require manual steps — running a console command, updating environment variables, or clearing cached data — before or after the Docker image update. Skipping these steps can leave your installation in a degraded state that is difficult to diagnose.
Establish a maintenance window for Appwrite upgrades — a predictable time each week or month when the team knows brief downtime may occur. For most self-hosted deployments, the upgrade process takes 5–15 minutes including the container restart. Communicate the window to users who depend on the service, or schedule it during low-traffic hours.
The Appwrite GitHub repository and Discord community are the best sources for upgrade guidance, migration tips, and troubleshooting help. The community is active and responsive — most questions get answered within hours. For teams running Appwrite in production, monitoring the GitHub releases feed keeps you informed of new versions as they ship.
Post-Migration Optimization
After a successful migration, there are several optimizations that make self-hosted Appwrite more reliable and performant than the default Docker installation.
Database connection pooling improves performance for applications with many concurrent users. Appwrite communicates with MariaDB directly, but for high-traffic deployments, adding a connection pooler like ProxySQL reduces the number of open database connections and prevents connection exhaustion under load. This is typically a concern at hundreds of concurrent users, not at initial launch, but planning for it means you know the scaling path when you need it.
CDN integration for storage files reduces latency for globally distributed users. Appwrite's storage API serves files directly from your server by default. For media files, images, and other assets accessed by many users, routing Appwrite's storage through a CDN (Cloudflare's free tier covers most use cases) moves asset serving to edge locations while keeping the metadata and authorization logic on your Appwrite server.
Index optimization matters for collections with complex queries. Appwrite's document model supports indexes on collection attributes, and creating the right indexes — composite indexes for multi-field queries, full-text indexes for search fields — dramatically improves query performance as collection sizes grow. Review your query patterns after migration and add indexes for the attribute combinations used in your most frequent queries.
Monitoring Appwrite in production means watching the MariaDB container's memory and query times, the Redis container for cache hit rates, and the worker containers for queue depth. Grafana and Prometheus work with Appwrite just as they do with any Docker-based deployment — connect node-exporter for host metrics and add database exporters for MariaDB metrics. The complete business stack guide covers the monitoring layer that ties these observability tools together.
Resource allocation across Appwrite's containers deserves deliberate planning. The main Appwrite container handles HTTP requests. The worker containers process background jobs — emails, webhooks, function executions. The executor container runs serverless functions in isolated Docker containers. Under heavy load, the worker and executor containers consume the most resources. Size your VPS based on your expected function execution frequency, not just your expected API request volume. A VPS with 4 GB RAM handles typical workloads comfortably; 8 GB provides headroom for function execution spikes and growing datasets.
Security Hardening for Self-Hosted Appwrite
Firebase handles infrastructure security automatically — DDoS protection, SSL termination, and account security are managed by Google. When you self-host Appwrite, these become your responsibility. The following hardening steps apply to most self-hosted Appwrite deployments.
API key scope limitation. Appwrite API keys are created per project with configurable scopes. Create separate API keys for each distinct part of your application — one key for your server-side backend with broad scopes, a separate key for your CI/CD pipeline with only the scopes needed for deployment tasks, and client-side SDKs that use the project's client key (which is scoped by Appwrite's platform restrictions rather than an API key). Avoid creating a single "master" API key used everywhere — if one component is compromised, limited-scope keys contain the blast radius.
Rate limiting at the reverse proxy layer. Appwrite's built-in rate limiting protects against some abuse patterns, but supplementing it with Caddy or Nginx rate limiting at the reverse proxy level provides defense in depth. Configure rate limits on authentication endpoints (login, registration, password reset) specifically — these are the highest-value abuse targets. A limit of 10 authentication requests per minute per IP prevents credential stuffing attacks without affecting legitimate users.
Audit logging and anomaly detection. Appwrite's console shows a log of recent authentication events and API calls. For production deployments, export these logs to your logging infrastructure (Loki, Elasticsearch, or a CloudWatch equivalent) for long-term retention and alerting. Set up alerts for anomalous patterns: authentication failures above a threshold, API calls from unexpected IP ranges, or sudden spikes in storage requests. These patterns often indicate either a bug in your application or an active attack.
Environment variable security. Appwrite's Docker Compose configuration contains sensitive values: the MariaDB root password, the Redis password, the Appwrite secret key (used for JWT signing), and SMTP credentials. Store these in a secrets manager (Vault, Infisical, or even a password manager) rather than in plaintext .env files on the server. Rotate the Appwrite secret key on a defined schedule — key rotation requires updating the environment variable and restarting the Appwrite containers, which invalidates all active sessions.
Container image updates and vulnerability scanning. Appwrite's Docker images receive security updates regularly. Subscribe to Appwrite's GitHub releases (watching for "Security" release notes) and apply patches within 7 days of release for production deployments. For teams with strict security requirements, integrate container image scanning (Trivy, Grype) into your CI pipeline to detect known vulnerabilities in the Appwrite container images before deploying updates.
For the complete self-hosted security checklist that applies to Appwrite and all other self-hosted tools, see self-hosting security checklist 2026. For teams evaluating Appwrite alongside Supabase and PocketBase for their specific use case, the best open source BaaS platforms 2026 provides a complete comparison with deployment requirements for each.
Compare BaaS platforms on OSSAlt — database models, auth options, and deployment side by side.
See open source alternatives to Firebase on OSSAlt.