Documentation
Guides
Migration & Portability

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

DirectionWhat it doesWorks out of the box?
Moontraze → Any platformExport data in universal formats (JSON, NDJSON, ZIP)Yes
Any platform → MoontrazeImport a moontraze-migrate packageRequires 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 hashes

Universal 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

OptionDescription
ModulesChoose Database / Storage / Rules / Auth
Include password hashesOpt-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.ndjson

Or 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.json

Or 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:

  1. Open your Moontraze project
  2. Go to Database → Migrate Import (package icon, top-right)
  3. Upload the ZIP
  4. Select modules to import
  5. (Optional) Enable Clear existing data — this deletes current data in target project
  6. 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:

  1. Export from Project A
  2. Delete the exported data from Project A
  3. Import into Project B

Or use Clear Existing in Project B if you're replacing data.

FAQ

QuestionAnswer
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