Firebase Multi Project Architecture
Large applications often need more than one Firebase project. A multi-project architecture separates environments — development, staging, and production — or splits a platform into isolated services. Keeping projects separate protects live user data from test activity and lets teams work independently without interfering with each other.
Why Multiple Projects Make Sense
Think of a hospital. The emergency room, the pharmacy, and the administrative office all serve the same institution but run as separate departments with separate records, separate access rules, and separate staff. Mixing them would create chaos. Multi-project Firebase architecture applies the same thinking to your app.
Single project (risky): Dev team runs tests ---> deletes real user data Load test ---> spikes production costs Broken migration ---> corrupts live database Multi-project (safe): my-app-dev ---> developers test freely here my-app-staging ---> QA and client review here my-app-prod ---> real users, real data, protected
Standard Three-Environment Setup
Project: my-app-dev Purpose: Local development and experimentation Users: Fake test accounts Data: Sample seed data, reset regularly Security Rules: Loose (allow read/write for easy testing) Billing: Spark (free) plan usually sufficient Project: my-app-staging Purpose: QA testing before each release Users: Realistic test accounts Data: Copy of production structure with anonymized data Security Rules: Match production exactly Billing: Blaze plan (mirrors production costs) Project: my-app-prod Purpose: Live users Users: Real accounts Data: Real user data Security Rules: Locked down, audited Billing: Blaze plan
Managing Multiple Projects with the Firebase CLI
The Firebase CLI stores project aliases in .firebaserc:
// .firebaserc
{
"projects": {
"default": "my-app-dev",
"staging": "my-app-staging",
"production": "my-app-prod"
}
}
Switch between projects during deployment:
# Deploy to development (default) firebase deploy # Deploy to staging firebase deploy --project staging # Deploy to production firebase deploy --project production
Managing Configuration per Environment
Each project has its own Firebase config object. Use environment variables to select the right config at build time:
// config/firebase.dev.js
export const firebaseConfig = {
apiKey: "dev-api-key",
projectId: "my-app-dev",
// ...
};
// config/firebase.prod.js
export const firebaseConfig = {
apiKey: "prod-api-key",
projectId: "my-app-prod",
// ...
};
// .env.development VITE_FIREBASE_PROJECT_ID=my-app-dev VITE_FIREBASE_API_KEY=dev-key // .env.production VITE_FIREBASE_PROJECT_ID=my-app-prod VITE_FIREBASE_API_KEY=prod-key
// firebase.js — reads from env variables
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
// ...
};
Your build tool (Vite, Create React App, Next.js) loads the correct .env file based on the build mode. Never commit .env.production to version control — use CI/CD secret management instead.
Multi-Project for Large Platforms
Beyond environments, some platforms use separate Firebase projects for different product lines:
Company: StudyPlatform Inc. Project: studyplatform-students ---> student-facing app Project: studyplatform-teachers ---> teacher dashboard Project: studyplatform-admin ---> internal admin tools
Separation ensures that a bug in the teacher dashboard cannot affect student data. Each project has its own database, its own security rules, and its own billing account — giving precise cost attribution per product.
Sharing Data Across Projects
Firebase does not natively share data between projects. To share data, use the Admin SDK on a server or Cloud Function that has access to both projects:
const admin = require("firebase-admin");
// Initialize two projects
const devApp = admin.initializeApp({
credential: admin.credential.cert(devServiceAccount),
projectId: "my-app-dev"
}, "dev");
const prodApp = admin.initializeApp({
credential: admin.credential.cert(prodServiceAccount),
projectId: "my-app-prod"
}, "prod");
const devDb = admin.firestore(devApp);
const prodDb = admin.firestore(prodApp);
// Copy data from dev to prod
async function migrateData() {
const snapshot = await devDb.collection("products").get();
const batch = prodDb.batch();
snapshot.docs.forEach((doc) => {
batch.set(prodDb.collection("products").doc(doc.id), doc.data());
});
await batch.commit();
console.log("Migration complete.");
}
Firestore Security Rules Across Projects
Security rules are project-specific. Keep rules in your repository and deploy them separately to each project:
firebase deploy --only firestore:rules --project staging firebase deploy --only firestore:rules --project production
Use the same rules file for staging and production. Differences between their rules can cause bugs that only appear in production — exactly the kind of surprise you want to avoid.
Key Takeaway
Multi-project architecture separates development, staging, and production environments into independent Firebase projects. Configure project aliases in .firebaserc and use environment variables to select the right Firebase config per build. Deploy to each project explicitly using the --project flag. Keep staging and production security rules identical, and migrate data between projects using the Admin SDK when necessary.
