# Migrating from MongoDB to FerretDB

> A practical guide to migrating from MongoDB to FerretDB, covering what works out of the box, what breaks, and how to check your codebase before switching.

Published: 2026-03-14
Canonical: https://layerbase.com/blog/migrating-from-mongodb-to-ferretdb

> **DocumentDB update:** Standalone DocumentDB is a separate engine from FerretDB, although both use PostgreSQL-based document storage. See the [MongoDB-compatible engine chooser](/create/mongodb) for current availability. The FerretDB examples, compatibility findings, migration instructions and benchmark results in this article do not establish DocumentDB support.

Short version: FerretDB implements the MongoDB wire protocol on top of PostgreSQL, so your driver and your `mongodb://` connection string stay as they are, and the data moves with `mongodump` and `mongorestore`, or in one pass through the [Atlas migration page](/migrate/mongodb-atlas) if you are landing on Layerbase Cloud. The copy is the mechanical part. The real work is the compatibility check, because `$lookup`, `$graphLookup`, change streams, `$text` search, capped collections, `$merge` and `$out`, `$jsonSchema` validation, and time-series collections are not supported, so grep for them before you touch any infrastructure. If none of them appear in your code, the migration is a connection-string change and a test run; if any of them are load-bearing, the right answer is to stay on MongoDB. Worth knowing before you plan around it: Layerbase does not host MongoDB itself, for licensing reasons, so the MongoDB card opens a [compatible-engine chooser](/create/mongodb). Select FerretDB explicitly for this migration guide.

If you've read our [MongoDB vs FerretDB comparison](/blog/mongodb-vs-ferretdb), 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](/blog/redis-vs-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](#what-works-out-of-the-box)
- [What Doesn't Work Yet](#what-doesnt-work-yet)
- [Check Your Codebase](#check-your-codebase)
- [Set Up FerretDB with the Layerbase CLI](#set-up-ferretdb-with-the-layerbase-cli)
- [Migrate Your Data](#migrate-your-data)
- [Point Your App at FerretDB](#point-your-app-at-ferretdb)
- [The PostgreSQL Bonus](#the-postgresql-bonus)
- [When to Stay on MongoDB](#when-to-stay-on-mongodb)
- [FAQ](#faq)
- [Wrapping Up](#wrapping-up)

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

| Feature                     | MongoDB                                   | FerretDB      | Workaround                                                                                                                                                                                                   |
| --------------------------- | ----------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`$lookup`**               | Cross-collection joins in aggregation     | Not supported | Two queries joined in application code, or query PostgreSQL directly                                                                                                                                         |
| **`$graphLookup`**          | Recursive graph traversal                 | Not supported | Application-level recursion, or PostgreSQL recursive CTEs                                                                                                                                                    |
| **Change streams**          | Real-time document change notifications   | Not supported | PostgreSQL `LISTEN`/`NOTIFY`                                                                                                                                                                                 |
| **`$text` search**          | Built-in text indexes and search          | Not supported | PostgreSQL `tsvector`/`tsquery`, or a search engine like [Meilisearch](/blog/getting-started-with-meilisearch)                                                                                               |
| **Capped collections**      | Fixed-size, insertion-order collections   | Not supported | Application-level cleanup, or PostgreSQL partitioning with retention policies                                                                                                                                |
| **`$merge` / `$out`**       | Write aggregation results to a collection | Not supported | Fetch results in app code and insert into target collection                                                                                                                                                  |
| **Schema validation**       | `$jsonSchema` on `createCollection`       | Not supported | Application-level validation, or PostgreSQL constraints on the underlying table                                                                                                                              |
| **Time-series collections** | Optimized time-series storage             | Not supported | PostgreSQL with [TimescaleDB](https://www.timescale.com/) extension, or a purpose-built time-series DB like [QuestDB](/blog/getting-started-with-questdb) or [InfluxDB](/blog/getting-started-with-influxdb) |
| **Atlas Search**            | Full-text and vector search (Atlas only)  | Not supported | PostgreSQL full-text search, pgvector, or dedicated search engines                                                                                                                                           |

The FerretDB team publishes a [compatibility page](https://docs.ferretdb.io/reference/supported-commands/) 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](/blog/getting-started-with-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 the Layerbase CLI

The fastest way to test the migration locally is [the Layerbase CLI](https://layerbase.com/docs/cli) (formerly SpinDB). One CLI, no Docker, no manual configuration. ([What is the Layerbase CLI?](https://layerbase.com/docs/cli))

Install the Layerbase CLI:

```bash
npm i -g layerbase    # npm
pnpm add -g layerbase # pnpm
```

Create a FerretDB instance:

```bash
lbase create ferret-migration -e ferretdb --start
```

The Layerbase CLI downloads FerretDB, provisions the PostgreSQL backend, and starts the server. Get the connection URL:

```bash
lbase 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

If you're moving to Layerbase Cloud, you can skip the manual steps below: paste your MongoDB connection string on the [MongoDB Atlas migration page](/migrate/mongodb-atlas) and Layerbase copies your collections, documents, and indexes into a managed FerretDB database, verifies the document counts against your source, and reports anything it couldn't bring across. It works with Atlas and self-hosted MongoDB alike.

For a local or self-managed FerretDB, 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 (measure it: our [MongoDB vs FerretDB benchmark](/blog/mongodb-vs-ferretdb-benchmark) and the runs on the [benchmarks hub](/benchmarks) show where each engine landed on one hardware profile)
- 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.

## FAQ

### How do I find out whether my app will actually run on FerretDB?

Grep first, then run your tests. The grep list in [Check Your Codebase](#check-your-codebase) takes five minutes and tells you whether any unsupported feature is in the codebase at all. After that, point a local FerretDB instance at your test suite: a failing test names the exact operation FerretDB does not implement, which is a far better inventory than reading a compatibility table and guessing. The data transfer is mechanical, so this is the step that decides whether the migration happens.

### What comes across in the copy, and what gets skipped?

Collections, documents, and ordinary indexes come across as they are. An index FerretDB cannot create is skipped in full rather than created without its options, and it is named in the migration report so you can decide whether to rebuild it by hand. Views and time-series collections are not copied and are listed the same way. Document counts are verified against the source when the copy finishes, and there is a ceiling: a collection over five million documents is refused, so a bigger move needs a conversation with support first.

### Does my MongoDB need to be offline while it copies?

It keeps serving, but the read is against your live database rather than a point-in-time snapshot, so writes landing partway through can arrive unevenly across collections. Copy from a quiet database, or pause writes for the duration. The cutover afterward is a connection string, since your driver does not know the backend changed.

### What do I use instead of change streams and `$text` once I am on FerretDB?

The replacements are all on the PostgreSQL side, which is the compensation for the gaps. Change streams become `LISTEN` and `NOTIFY`, or polling if the use case is cache invalidation. `$text` becomes PostgreSQL full-text search over the underlying JSONB, or a dedicated engine like [Meilisearch](/blog/getting-started-with-meilisearch) if search is a real feature rather than a lookup box. `$lookup` becomes two queries joined in application code, or a SQL join against the tables directly.

### Why not just run MongoDB on Layerbase and skip all of this?

MongoDB's license does not permit it, which is the whole reason FerretDB is here. The MongoDB card opens a chooser for compatible engines. Select FerretDB explicitly to follow this guide. If the compatibility gaps rule FerretDB out for your workload, the correct move is to stay where you are rather than force the migration.

## 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
lbase stop ferret-migration    # Stop FerretDB
lbase start ferret-migration   # Start FerretDB
lbase connect ferret-migration # Connect with mongosh
lbase list                     # See all your database instances
```

The Layerbase CLI supports [20+ engines](https://layerbase.com/docs/cli), so you can test both MongoDB and FerretDB side by side locally before committing to the migration. For a hosted option, [Layerbase Cloud](https://cloud.layerbase.com) provisions FerretDB in seconds (MongoDB itself is not creatable in the cloud for licensing reasons; the MongoDB card opens a chooser for compatible engines):

- [Create a FerretDB instance](https://cloud.layerbase.com/create?engine=ferretdb)

Or install [Layerbase Desktop](https://layerbase.com/desktop/download) for a visual interface to manage your local databases.
