# Database Migrations

Back up and migrate MongoDB across versions


How erxes handles MongoDB schema and data changes during upgrades.

For image selection and rollback planning, see [Upgrades](https://erxes.io/docs/self-hosting-upgrades). For the databases involved, see [Configuration](https://erxes.io/docs/self-hosting-configuration) and [Background Services](https://erxes.io/docs/architecture-background-services).

## How migrations work

There is **no global migration framework**: no `migrate up` CLI, no version table, no automatic run on deploy. Migrations are standalone TypeScript scripts that each open their own MongoDB connection (reading `MONGO_URL`, sometimes a domain override like `CORE_MONGO_URL`), do their work, and call `process.exit()`. You run them deliberately, per release that needs one. Three kinds exist:

1. **Plugin-owned scripts**: live inside the plugin whose data they change, for example `backend/plugins/operation_api/src/migrations/` (`migrateTasks.ts`, `migrateCycle.ts`), `backend/plugins/frontline_api/src/migrations/` (ten scripts), and `backend/plugins/content_api/src/modules/cms/migrations/`. They are plain scripts you run with `tsx` against the deployment's `MONGO_URL`; there is no registration step.
2. **The consolidated `saas-migrations` package**: `backend/saas-migrations` holds one-off data migrations organized per domain (`core`, `content`, `frontline`, `operation`, `posclient`, `products`, `sales`, plus domains for Enterprise plugins). It includes a unified runner, [run.ts](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/backend/saas-migrations/run.ts), that discovers `*.ts` files under each domain directory and executes each in its own `node --import tsx` child process (required because the scripts self-invoke and exit). `core` runs first, then the rest alphabetically.
3. **`migrations.json` at the repository root**: an Nx workspace artifact listing code migrations for contributors upgrading Nx packages. It is not a product database mechanism; ignore it for deployment planning.

A migration is only relevant if the release you are moving to includes one. Check the release notes and the `saas-migrations` directory diff for your version jump; most releases add no migration at all.

## Running the consolidated runner

From a machine that can reach the database (a checkout, or the `erxes/erxes-next-migrations` image published by `ci-saas-migrations.yml`):

```bash
# Preview order, run nothing
tsx backend/saas-migrations/run.ts --list

# Everything, core first; stops on the first failure
tsx backend/saas-migrations/run.ts

# Only some domains, or a single script
tsx backend/saas-migrations/run.ts core frontline
tsx backend/saas-migrations/run.ts sales/migrateSales.ts

# Keep going past failures
tsx backend/saas-migrations/run.ts --continue
```

Or from `backend/saas-migrations`: `pnpm migrate` and `pnpm migrate:list`. The runner exits non-zero if any script fails.

## Backup before anything writes

Dump the application database **and** its logs database. The logs service writes to `<db>_logs` (for example `erxes_logs`; see [connectionResolvers.ts](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/backend/services/logs/src/connectionResolvers.ts)) and undo history lives there:

```bash
mongodump --uri="mongodb://<user>:<password>@<hosts>/erxes?replicaSet=<rs>&authSource=admin" \
  --db=erxes --archive=erxes-<date>.archive.gz --gzip
mongodump --uri="mongodb://<user>:<password>@<hosts>/erxes?replicaSet=<rs>&authSource=admin" \
  --db=erxes_logs --archive=erxes-logs-<date>.archive.gz --gzip
```

Verify a restore on staging before relying on it:

```bash
mongorestore --uri="<staging-uri>" --archive=erxes-<date>.archive.gz --gzip \
  --nsFrom='erxes.*' --nsTo='erxes_restore.*'
```

Preserve Redis state too if queued BullMQ work matters to your recovery plan.

## Upgrade sequence with migrations

### 1. Record and back up

Note the running image digests, take the dumps above, and verify the backup restores.

### 2. Check what the release ships

Read `CHANGELOG.md` for your version jump and diff `backend/saas-migrations/` and plugin `src/migrations/` directories between the two tags. If nothing changed, no migration step is needed.

### 3. Stage it

Restore production data to staging, deploy the candidate image set, run the relevant migration scripts, and exercise login, enabled plugins, automations, and file access.

### 4. Run in production

Stop writes or use a maintenance window if the migration rewrites data. Run the scripts, then deploy the new image set together; do not mix a new Core API with old plugin APIs unless the release notes allow it. Watch the gateway, Core API, plugin, and logs-service output for migration or composition errors.

### 5. Verify

Check `/health`, `/graphql`, owner login, and one write per enabled domain. Keep the pre-upgrade backup until the new version is confirmed.

> [!WARNING]
> **Migrations are not reversible by redeploying**
>
> Rolling the containers back to the previous image does not undo data changes a migration already wrote. Rollback after a data migration means restoring the pre-upgrade backup and losing every write since. Treat migration scripts as one-way and test them in staging first.

