Migration & Portability
Complete guide to exporting your Moontraze data to any platform, and importing data from other platforms into Moontraze.
Overview
Moontraze migrate is designed for data portability — move your data in or out of Moontraze anytime. No vendor lock-in. Migration is different from Backup/Recovery — Backup/Restore is for restoring inside Moontraze, while Migration is for moving data across platforms.
Two Directions
| Direction | What it does | Works out of the box? |
|---|---|---|
| Moontraze → Any platform | Export data in universal formats (JSON, NDJSON, ZIP) | Yes |
| Any platform → Moontraze | Import a moontraze-migrate package | Requires conversion first |
Export from Moontraze
What You Get
Export from Dashboard → Database → Migrate Export. You get a ZIP file:
moontraze-migrate-xxxx.zip
├── manifest.json # Metadata
├── README.md # Instructions
├── schema.postgres.stub.sql # Starter Postgres schema
├── database/
│ ├── users.ndjson # One JSON object per line
│ └── posts.ndjson
├── storage/
│ ├── meta.json # File metadata
│ └── files/... # Original files (raw bytes)
├── rules/
│ └── rules.json # Rule definitions
└── auth/
└── users.json # Users + bcrypt password hashesUniversal Formats Used
*.ndjson— Newline-delimited JSON. Every programming language can parse this.*.json— Standard JSON.files/— Raw binary files, upload directly to any bucket.
Export Options
| Option | Description |
|---|---|
| Modules | Choose Database / Storage / Rules / Auth |
| Include password hashes | Opt-in. If not selected, users must reset passwords |
Importing Moontraze Data into Other Platforms
Moontraze does not write platform-specific import scripts for every BaaS. Data is delivered in universal formats — you write a small converter for your target.
To Self-Hosted Postgres
Use the included schema.postgres.stub.sql as a starting point, then load ndjson.
# Create your tables
psql -U postgres -d yourdb -f schema.postgres.stub.sql
# Load each collection
while IFS= read -r line; do
echo "$line" | jq -c '.' | psql -U postgres -d yourdb -c \
"INSERT INTO app_docs (id, collection, data) VALUES ('$(echo $line | jq -r .id)', 'users', '$(echo $line | jq -c .)')"
done < database/users.ndjsonOr in Node.js:
const fs = require('fs');
const { Client } = require('pg');
const client = new Client({ connectionString: process.env.DATABASE_URL });
await client.connect();
const lines = fs.readFileSync('database/users.ndjson', 'utf8').split('\n').filter(Boolean);
for (const line of lines) {
const doc = JSON.parse(line);
await client.query(
'INSERT INTO app_docs (id, collection, data) VALUES ($1, $2, $3)',
[doc.id, 'users', doc]
);
}To Firebase Firestore
const admin = require('firebase-admin');
const fs = require('fs');
admin.initializeApp({ credential: admin.credential.applicationDefault() });
const db = admin.firestore();
const lines = fs.readFileSync('database/users.ndjson', 'utf8').split('\n').filter(Boolean);
const batch = db.batch();
for (const line of lines) {
const doc = JSON.parse(line);
const ref = db.collection('users').doc(String(doc.id));
batch.set(ref, doc);
}
await batch.commit();To MongoDB
# Convert ndjson → JSON array
jq -s '.' database/users.ndjson > users.json
# Import
mongoimport --db=yourdb --collection=users --jsonArray users.jsonOr Node.js:
const { MongoClient } = require('mongodb');
const fs = require('fs');
const client = new MongoClient(process.env.MONGO_URL);
await client.connect();
const db = client.db('yourdb');
const lines = fs.readFileSync('database/users.ndjson', 'utf8').split('\n').filter(Boolean);
const docs = lines.map(l => JSON.parse(l));
await db.collection('users').insertMany(docs);To Supabase
Supabase uses Postgres under the hood, so the Postgres example above applies. Or use the SDK:
import { createClient } from '@supabase/supabase-js';
import fs from 'fs';
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY);
const lines = fs.readFileSync('database/users.ndjson', 'utf8').split('\n').filter(Boolean);
const docs = lines.map(l => JSON.parse(l));
const { error } = await supabase.from('users').insert(docs);
if (error) console.error(error);To Storage Buckets
Files are raw bytes in storage/files/. Upload directly.
# AWS S3
aws s3 cp storage/files/ s3://your-bucket/ --recursive
# Google Cloud Storage
gsutil -m cp -r storage/files/* gs://your-bucket/Auth Users
auth/users.json contains bcrypt hashes. If your target platform uses bcrypt, hashes work as-is — users keep their passwords. Otherwise users must reset passwords. Never share auth/users.json publicly — it contains password hashes.
Import into Moontraze
Moontraze import expects the moontraze-migrate package format. To import data from another platform, you must first convert it.
Required Package Structure
your-migrate-package.zip
├── manifest.json # REQUIRED
├── database/
│ └── <collection>.ndjson # One JSON per line
├── rules/
│ └── rules.json # Optional
├── auth/
│ └── users.json # Optional
└── storage/
├── meta.json # Optional
└── files/...manifest.json Format
{
"type": "moontraze-migrate",
"version": 1,
"exportedAt": "2026-09-18T10:00:00.000Z",
"projectId": "TARGET_PROJECT_ID",
"modules": {
"database": true,
"storage": true,
"rules": true,
"auth": true
},
"includePasswordHashes": true,
"stats": {
"collections": 1,
"documents": 100,
"users": 10
},
"database": [
{ "name": "users", "file": "users.ndjson", "count": 100 }
]
}Document Format
One JSON object per line. Each document must have an id:
{"id":"doc1","name":"Alice","age":30}
{"id":"doc2","name":"Bob","age":25}Auth Format
{
"type": "auth",
"version": 1,
"includePasswordHashes": true,
"users": [
{
"id": "user1",
"email": "alice@example.com",
"name": "Alice",
"password": "$2a$10$...",
"role": "user"
}
]
}Rules Format
{
"type": "rules",
"version": 1,
"rules": [
{
"path": "/users/{uid}",
"readCondition": "auth.uid === uid",
"writeCondition": "auth.uid === uid",
"description": "Users can read/write own document"
}
]
}Storage Format
{
"type": "storage",
"version": 1,
"folders": [
{ "id": "folder1", "name": "avatars", "parentId": null }
],
"files": [
{
"id": "file1",
"name": "avatar.png",
"path": "/users/avatar.png",
"size": 12345,
"mimeType": "image/png",
"folderId": "folder1"
}
]
}Converting From Other Platforms
From Firebase Firestore
const admin = require('firebase-admin');
const fs = require('fs');
admin.initializeApp({ credential: admin.credential.applicationDefault() });
const db = admin.firestore();
// 1. Export each collection
const collections = ['users', 'posts', 'comments'];
fs.mkdirSync('database', { recursive: true });
for (const name of collections) {
const snap = await db.collection(name).get();
const lines = snap.docs.map(d => JSON.stringify({ id: d.id, ...d.data() }));
fs.writeFileSync(`database/${name}.ndjson`, lines.join('\n'));
}
// 2. Create manifest
const manifest = {
type: 'moontraze-migrate',
version: 1,
exportedAt: new Date().toISOString(),
projectId: 'YOUR_MOONTRAZE_PROJECT_ID',
modules: { database: true, storage: false, rules: false, auth: false },
stats: { collections: collections.length },
database: collections.map(name => ({ name, file: `${name}.ndjson`, count: 0 }))
};
fs.writeFileSync('manifest.json', JSON.stringify(manifest, null, 2));
// 3. Zip
// Use archiver or zip -r package.zip .From MongoDB
# 1. Export as JSON array
mongoexport --db=yourdb --collection=users --jsonArray --out=users.json
# 2. Convert to ndjson (one per line)
jq -c '.[]' users.json > database/users.ndjson
# 3. Create manifest.json (see format above)
# 4. Zip
zip -r package.zip manifest.json database/From Self-Hosted Postgres
# 1. Export each table as ndjson
psql -U postgres -d yourdb -c "COPY (SELECT row_to_json(t) FROM users t) TO STDOUT" \
| sed 's/\\n/\n/g' > database/users.ndjson
# 2. Create manifest.json
# 3. Zip
zip -r package.zip manifest.json database/From Supabase
Supabase is Postgres, so the Postgres example applies. Or use the SDK:
import { createClient } from '@supabase/supabase-js';
import fs from 'fs';
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY);
const { data: users } = await supabase.from('users').select('*');
fs.mkdirSync('database', { recursive: true });
fs.writeFileSync(
'database/users.ndjson',
users.map(u => JSON.stringify(u)).join('\n')
);Import into Moontraze Dashboard
Once you have a moontraze-migrate package:
- Open your Moontraze project
- Go to Database → Migrate Import (package icon, top-right)
- Upload the ZIP
- Select modules to import
- (Optional) Enable Clear existing data — this deletes current data in target project
- Click Import
Cross-Project Migration
If you're moving data from Project A to Project B in Moontraze, delete the users from Project A first. Otherwise you'll get an id conflict error — because user IDs are globally unique.
Recommended workflow:
- Export from Project A
- Delete the exported data from Project A
- Import into Project B
Or use Clear Existing in Project B if you're replacing data.
FAQ
| Question | Answer |
|---|---|
| Can I import Firebase/MongoDB/Supabase data directly? | No — convert to moontraze-migrate format first |
| What happens to password hashes? | Bcrypt hashes carry over if target uses bcrypt; otherwise users reset |
| Can I migrate between Moontraze projects? | Yes — see Cross-Project Migration above |
| Is the migrate package encrypted? | No — it's plain JSON/ZIP. Encrypt the ZIP after export if needed |
| What if I have huge collections? | database/*.ndjson is streamed — no size limit. Import handles batches of 150 docs |
For encrypted backups inside Moontraze, use Backup → Encrypted instead of Migrate.
Related
- Backup & Recovery — For encrypted backups inside Moontraze
- Storage — File storage
- Authentication — User management