> 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/getting-started/getting-started/docker-compose.md).

# Docker Compose

Self-hosted, no Kubernetes, no Prometheus or Grafana required. In this mode the logs are the whole observability story — see [Logs](/mongo2pg/observability/observability/dashboards-and-logs.md#logs).

## The stack

`deploy/compose/docker-compose.mongo2pg.yaml` ships a PostgreSQL (the migration target) and one `mongo2pg-<service>` container per Atomic Blend service, each mounting its own mapping file from `deploy/compose/mappings/`. Everything sits behind a `migration` Compose profile, so a self-hoster who is not migrating never starts a Postgres or a replicator and pays nothing for the feature existing.

```mermaid
flowchart TB
    subgraph Profile["docker compose --profile migration"]
        SRC["mongo2pg-source-mongo<br/>(standalone fixture only —<br/>a real install reuses backend/docker's mongodb)"]
        PG["postgres<br/>one CREATE DATABASE per service<br/>(postgres-init.sh)"]
        A["mongo2pg-auth<br/>run --config=/etc/mongo2pg/config.yaml<br/>--metrics-addr=:9090"]
        P["mongo2pg-productivity<br/>run --config=/etc/mongo2pg/config.yaml<br/>--metrics-addr=:9090"]
    end

    CFA["./mappings/auth.yaml<br/>mounted read-only"] -.-> A
    CFP["./mappings/productivity.yaml<br/>mounted read-only"] -.-> P

    SRC -- "depends_on: service_healthy" --> A
    SRC -- "depends_on: service_healthy" --> P
    PG -- "depends_on: service_healthy" --> A
    PG -- "depends_on: service_healthy" --> P

    A -- "MONGO_URI, DATABASE_NAME" --> SRC
    P -- "MONGO_URI, DATABASE_NAME" --> SRC
    A -- "PG_DSN=.../auth" --> PG
    P -- "PG_DSN=.../productivity" --> PG

    A -. ":9090/metrics<br/>reachable, nothing scrapes it" .-> EXT["any Prometheus already<br/>on this compose network"]
```

```bash
cd deploy/compose
cp .env.example .env          # fill in MONGO_*, POSTGRES_* and the mapping paths
docker compose --env-file .env -f docker-compose.mongo2pg.yaml --profile migration up -d
docker compose -f docker-compose.mongo2pg.yaml logs -f mongo2pg-productivity
```

The self-hosted stack runs a single MongoDB database (`DATABASE_NAME`, default `atomic-blend`) shared by every service — production splits one database per service instead (see [Cross-service references](/mongo2pg/concepts/concepts/cross-service-references.md)). The mapping files under `deploy/compose/mappings/` therefore all point `source.database` at that same shared database and declare **disjoint** `collections:` sets; each replicator opens its own change stream on the shared database, filtered to its own collections.

## Following a migration

You will see progress on an interval, not a wall of per-document lines:

```json
{"collection":"tasks","done":48000,"total":202000,"percent":23.8,"rate_per_sec":3200,"eta":48000,"message":"snapshot progress"}
{"lag_seconds":0.2,"quarantine":0,"pending_fk":0,"ready":"8/8","message":"streaming"}
{"collections":8,"message":"CUTOVER READY — all collections caught up, quarantine empty"}
```

The rate is a sliding window rather than a cumulative average, so a slowdown shows up within one interval instead of being diluted by hours of good history. `--progress-interval` controls how often a line is emitted (the compose stack tightens it to `5s`; the CLI's own default is `15s`).

Run any other command against the same container once it is up:

```bash
docker compose -f docker-compose.mongo2pg.yaml run --rm mongo2pg-productivity status
docker compose -f docker-compose.mongo2pg.yaml run --rm mongo2pg-productivity cutover check
docker compose --profile migration down
```

`/metrics` (`:9090`) stays reachable to any Prometheus already attached to the compose network — nothing in the compose stack depends on it, but nothing stops you scraping it either.

## What's different from production

Production splits MongoDB one database per service (see [Cross-service references](/mongo2pg/concepts/concepts/cross-service-references.md)); the self-hosted stack runs one shared database with disjoint collection sets instead. Cross-service references still resolve correctly in both layouts, because they resolve by pure derivation, never by reaching into another service's database.


---

# 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/getting-started/getting-started/docker-compose.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.
