Migrating from MongoDB to FerretDB

MongoDBFerretDBDatabases

If you've read our MongoDB vs FerretDB comparison, you know FerretDB implements the MongoDB wire protocol on top of PostgreSQL. Same mongodb:// connection string, same driver, Apache 2.0 licensed. But knowing that FerretDB exists and actually migrating a production app to it are two different things.

Unlike Redis to Valkey, where you literally swap the connection string and everything works, MongoDB to FerretDB has real compatibility gaps. FerretDB covers a large subset of the MongoDB API, but it is not a complete drop-in. The migration is feasible for many applications. You just need to check your queries first.

Contents

What Works Out of the Box

FerretDB supports the core operations that most applications rely on. If your codebase uses standard CRUD and basic aggregation, the migration is straightforward.

CRUD operations:

  • insertOne, insertMany
  • find, findOne with projections and sorting
  • updateOne, updateMany with $set, $unset, $inc, $push, $pull, $addToSet
  • deleteOne, deleteMany
  • replaceOne

Query operators:

  • Comparison: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin
  • Logical: $and, $or, $not, $nor
  • Element: $exists, $type
  • Array: $elemMatch, $size, $all
  • Regex: $regex

Aggregation stages:

  • $match, $group, $sort, $project, $unwind
  • $limit, $skip, $count

Other:

  • Array queries (matching elements, nested arrays)
  • Dot notation for nested documents
  • Index creation (createIndex)
  • countDocuments, estimatedDocumentCount
  • distinct

This covers the majority of what typical CRUD applications use. If your app inserts documents, queries them with filters, runs some aggregation for reporting, and updates records, you're likely in the clear.

What Doesn't Work Yet

Here is where you need to pay attention. These MongoDB features are not supported in FerretDB as of early 2026:

FeatureMongoDBFerretDBWorkaround
$lookupCross-collection joins in aggregationNot supportedTwo queries joined in application code, or query PostgreSQL directly
$graphLookupRecursive graph traversalNot supportedApplication-level recursion, or PostgreSQL recursive CTEs
Change streamsReal-time document change notificationsNot supportedPostgreSQL LISTEN/NOTIFY
$text searchBuilt-in text indexes and searchNot supportedPostgreSQL tsvector/tsquery, or a search engine like Meilisearch
Capped collectionsFixed-size, insertion-order collectionsNot supportedApplication-level cleanup, or PostgreSQL partitioning with retention policies
$merge / $outWrite aggregation results to a collectionNot supportedFetch results in app code and insert into target collection
Schema validation$jsonSchema on createCollectionNot supportedApplication-level validation, or PostgreSQL constraints on the underlying table
Time-series collectionsOptimized time-series storageNot supportedPostgreSQL with TimescaleDB extension, or a purpose-built time-series DB like QuestDB or InfluxDB
Atlas SearchFull-text and vector search (Atlas only)Not supportedPostgreSQL full-text search, pgvector, or dedicated search engines

The FerretDB team publishes a compatibility page that tracks supported commands and operators. Bookmark it and check before migrating.

Check Your Codebase

Before you touch any infrastructure, grep your codebase for unsupported operations. This takes five minutes and tells you exactly how feasible the migration is.

bash
# Search for unsupported MongoDB features
grep -rn '\$lookup' src/
grep -rn '\$graphLookup' src/
grep -rn '\.watch(' src/
grep -rn '\$text' src/
grep -rn '\$merge' src/
grep -rn '\$out' src/
grep -rn 'capped.*true' src/
grep -rn '\$jsonSchema' src/

If none of those return results, your migration path is clean. Swap the connection string, run your test suite, and you're done.

If they do return results, you need to assess each usage:

  • $lookup in a few places? Refactor to two queries joined in your application code. It's more code, but it works identically on both engines.
  • Change streams for cache invalidation? Switch to polling, or use PostgreSQL LISTEN/NOTIFY after migrating.
  • $text for search? This is often the hardest one to replace inline. Consider adding Meilisearch or using PostgreSQL full-text search on the underlying tables.

The key question: are the unsupported features load-bearing in your application, or are they convenience features you can work around?

Set Up FerretDB with SpinDB

The fastest way to test the migration locally is SpinDB. One CLI, no Docker, no manual configuration. (What is SpinDB?)

Install SpinDB:

bash
npm i -g spindb    # npm
pnpm add -g spindb # pnpm

Create a FerretDB instance:

bash
spindb create ferret-migration -e ferretdb --start

SpinDB downloads FerretDB, provisions the PostgreSQL backend, and starts the server. Get the connection URL:

bash
spindb url ferret-migration
text
mongodb://127.0.0.1:27018

That's a standard mongodb:// URL. Your existing MongoDB driver connects to it without any code changes.

Migrate Your Data

Use mongodump to export your data from MongoDB and mongorestore to import it into FerretDB. These are standard MongoDB tools, and FerretDB supports the wire protocol they use.

Step 1: Dump from MongoDB

bash
mongodump --uri="mongodb://localhost:27017" --db=myapp --out=./dump

This creates a ./dump/myapp/ directory with BSON files for each collection.

If your MongoDB instance requires authentication:

bash
mongodump --uri="mongodb://user:pass@your-mongo-host:27017/myapp?authSource=admin" --out=./dump

Step 2: Restore into FerretDB

bash
mongorestore --uri="mongodb://localhost:27018" --db=myapp ./dump/myapp/

FerretDB accepts the same mongorestore protocol. Your documents get stored as JSONB in the underlying PostgreSQL database.

Step 3: Verify the data

Connect with mongosh and spot-check:

bash
mongosh "mongodb://localhost:27018/myapp"
javascript
db.users.countDocuments()
db.users.findOne()
db.orders.countDocuments()

Compare the counts and a few sample documents against your MongoDB source. If they match, your data is in FerretDB.

Point Your App at FerretDB

Update your connection string. That's it. Your MongoDB driver doesn't know or care that the backend changed.

typescript
// Before
const MONGO_URL = 'mongodb://localhost:27017/myapp'

// After
const MONGO_URL = 'mongodb://localhost:27018/myapp'

Now run your test suite:

bash
pnpm test

If all tests pass, you're migrated. If some tests fail, the failures will point you to exactly which MongoDB features you're using that FerretDB doesn't support yet.

This is why running tests is the real migration step. The data transfer is mechanical. The compatibility check is what matters.

The PostgreSQL Bonus

Once your data is in FerretDB, it lives in PostgreSQL. This unlocks things MongoDB can't offer.

Query with SQL. Your documents are stored as JSONB. You can query them with standard SQL:

sql
SELECT _jsonb->>'title' AS title, _jsonb->>'status' AS status
FROM myapp.tasks_aaa30e43
WHERE _jsonb->>'priority' = 'high';

The table names are auto-generated by FerretDB, but you can find them with \dt in psql.

Use pg_dump for backups. Your existing PostgreSQL backup scripts work on FerretDB's data. No new tools, no new processes:

bash
pg_dump -h localhost -U postgres ferretdb_db > backup.sql

Access the extension ecosystem. PostGIS for geospatial queries. pgvector for embeddings. pg_cron for scheduled jobs. TimescaleDB for time-series. These extensions work alongside your FerretDB data because it's all PostgreSQL under the hood.

Consolidate infrastructure. If you're already running PostgreSQL for your relational data, FerretDB means you don't need a second database system. One backup strategy, one monitoring setup, one set of operational playbooks.

When to Stay on MongoDB

Be honest with yourself about your requirements. FerretDB is not ready for every workload.

Stay on MongoDB if:

  • You rely on change streams for real-time features (live dashboards, event-driven architectures)
  • You use $lookup heavily across many collections and refactoring would be impractical
  • You depend on Atlas Search for full-text or vector search
  • You use time-series collections for IoT or metrics data
  • Write throughput is critical and you need WiredTiger's purpose-built performance
  • Your team is invested in the Atlas ecosystem (Charts, Realm, App Services)

FerretDB is actively developing. Features like $lookup and change streams may arrive in future releases. But "may arrive later" is not a migration plan. Base your decision on what works today.

Wrapping Up

Migrating from MongoDB to FerretDB is straightforward if your application uses standard CRUD operations and basic aggregation. The process is: check your codebase for unsupported features, set up FerretDB, dump and restore your data, swap the connection string, run your tests.

The compatibility gaps are real, and you should respect them. But for applications that don't use $lookup, change streams, or text search, the migration removes the SSPL licensing concern and consolidates your data onto PostgreSQL infrastructure.

Manage your local instances:

bash
spindb stop ferret-migration    # Stop FerretDB
spindb start ferret-migration   # Start FerretDB
spindb connect ferret-migration # Connect with mongosh
spindb list                     # See all your database instances

SpinDB supports 20+ engines, so you can test both MongoDB and FerretDB side by side before committing to the migration. For a hosted option, Layerbase Cloud provisions either engine in seconds:

Or install Layerbase Desktop for a visual interface to manage your local databases.

Something not working?