> For the complete documentation index, see [llms.txt](https://atomic-blend.gitbook.io/mongo2pg/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://atomic-blend.gitbook.io/mongo2pg/readme.md).

# mongo2pg

mongo2pg migrates a MongoDB database to PostgreSQL **without downtime** — and proves the two agree before you switch.

One instance replicates one service: it generates the PostgreSQL schema from a declarative mapping file, backfills every existing document, then tails MongoDB change streams until both sides are verifiably in sync. Only then do you cut the application over and retire the tool.

```mermaid
flowchart LR
    subgraph Mongo["MongoDB"]
        M[(source database)]
    end
    subgraph Tool["mongo2pg — one process per service"]
        SR[snapshot reader] --> MAP[mapper<br/>coercion · nesting · transforms]
        CS[change stream] --> MAP
        MAP --> RES[identity resolver<br/>pk generation · fk relinking]
        RES --> WR[PG writer<br/>COPY / guarded upsert]
    end
    subgraph PG["PostgreSQL"]
        T[(target tables)]
        S[(_migration schema<br/>checkpoint · id_map · quarantine)]
    end

    M -- documents --> SR
    M -- change events + resume token --> CS
    WR -- one transaction per batch --> T
    WR -- same transaction --> S

    CHK["mongo2pg cutover check"] -.gates.-> FLIP["flip the application"]
```

`cutover check` exits `0` only when every collection is streaming and caught up, quarantine and pending foreign keys are empty, the requested verification checks passed, and every deferred foreign key has validated — see [What checks it performs](/mongo2pg/operating-a-migration/operations/verification-and-checks.md).

## Why not Debezium or Airbyte?

They are good tools, and for a straight table-to-table copy they are the better answer. mongo2pg exists for three problems they do not solve:

* **Heterogeneous field types.** A field that is a string in 6,026 documents and a date in 1,800 cannot become one native PostgreSQL column without per-field coercion rules. Real collections look like this after a few years of schema drift and half-finished migrations at the application layer.
* **Changing the primary key type, and relinking every reference.** ObjectIDs become UUIDv7 derived deterministically from the ObjectID's own embedded timestamp, and every foreign key — including one that points across a service boundary into a *different* database — is rewritten to match, with no cross-database lookup required.
* **A cutover gate you can put in a deploy script.** `mongo2pg cutover check` exits non-zero until it is genuinely safe to switch, and names exactly which condition failed for which collection.

**Use Debezium (or Airbyte, or a one-off script) instead if:**

* Your MongoDB documents already map cleanly onto a table, field types are already consistent, and you are keeping the same identifier scheme. mongo2pg's coercion engine, primary-key derivation, and quarantine machinery are all solving problems you don't have.
* You want a permanent, bidirectional, or multi-target replica. mongo2pg is one-way and ends in a cutover — there is no reverse sync, no dual-write, and nothing writes back to MongoDB, ever.
* You need a general-purpose CDC/ETL platform for many source and sink types. mongo2pg is Mongo-to-Postgres only, by design, so that the identity and coercion model can be this specific.
* You would rather operate a Kafka Connect cluster than a single stateless Go binary whose entire state lives in the target PostgreSQL database.

## How it works

| Concern                       | Approach                                                                                                                           | Code                |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| Mixed BSON types → one column | Ordered coercion rules per field; the first whose type and format match wins                                                       | `internal/coerce`   |
| ObjectID → UUIDv7             | Timestamp from the ObjectID, entropy from `sha256(namespace‖id)` — deterministic, so two services derive the same id independently | `internal/pk`       |
| Foreign keys                  | Rewritten through the same derivation; unresolvable ones deferred, not dropped                                                     | `internal/resolver` |
| Change data capture           | Resume token committed in the same transaction as the rows it accounts for                                                         | `internal/pipeline` |
| Schema                        | Generated as [tern](https://github.com/jackc/tern) migrations you can review before applying                                       | `internal/schema`   |
| Verification                  | Counts, `_id`-range checksums, and samples re-mapped through the live mapper                                                       | `internal/verify`   |

PostgreSQL is driven through [pgx](https://github.com/jackc/pgx) natively — `CopyFrom` for backfill, batched writes for CDC — never `database/sql`.

## Where to go next

* New to the tool? Start with [Getting started](/mongo2pg/getting-started/getting-started.md).
* Writing a mapping file? [Concepts](/mongo2pg/concepts/concepts.md) explains the model, then [The mapping reference](/mongo2pg/reference/mapping-reference.md) documents every key.
* Running a live migration? [Operating a migration](/mongo2pg/operating-a-migration/operations.md) covers the CLI, the cutover procedure, and what to do when something is quarantined.
* Wiring up dashboards and alerts? See [Observability](/mongo2pg/observability/observability.md).
* Contributing to the tool itself? See [Architecture](/mongo2pg/contributing/architecture.md) and [Development](/mongo2pg/contributing/development.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://atomic-blend.gitbook.io/mongo2pg/readme.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
