---
title: "Database Migrations"
description: "The Enterprise Stack includes a MongoDB migration system that keeps the database structure, indexes, and stored data in sync with the version of the Enterprise API you are running."
stack: "Enterprise Stack (commercial) — version 0.21.0"
stack_version: "0.21.0"
stack_comparison: https://docs.walt.id/community-vs-enterprise.md
canonical_url: https://docs.walt.id/enterprise-stack/administration/data/db-migrations
generated: 2026-07-20
---
# Database Migrations

The Enterprise Stack includes a MongoDB migration system that keeps the database structure, indexes, and stored data in
sync with the version of the Enterprise API you are running.

Migrations are executed automatically when the Enterprise API starts. They can also be inspected and scheduled manually
through the global administration API.

**Info:**

Database migrations are currently supported for MongoDB deployments. The in-memory database does not support migration
execution.

## How Migrations Work

Each migration has a numeric version, a description, a list of affected collections, and a set of allowed actions.
Migrations are registered in version order and are designed to be safe to run in clustered deployments.

When an Enterprise API node starts:

1. The node connects to MongoDB.
2. The migration framework checks the current cluster database version.
3. Required startup migrations are executed before the application continues.
4. Background migrations are started asynchronously after startup.
5. The node records progress and results in the `migrations` collection.

Only one node executes migrations at a time. The migration system uses a MongoDB-backed lock named `migration_lock` in the
`cluster_locks` collection. Other nodes wait until the lock is released or expires.

## Migration Types

Migrations are grouped by impact:

- `SCHEMA` - creates or updates collections and document structure.
- `INDEX` - creates, updates, or drops database indexes.
- `DATA` - transforms existing records. These migrations can take longer on large collections.

## Automatic Startup Behavior

Most migrations are applied automatically. The Enterprise API distinguishes between two execution modes:

- **Startup migrations** run before the application completes database initialization.
- **Background migrations** run after startup, so the API can become available while lower-risk migrations continue.

Background migrations are used when the application can safely handle both the old and new data shape during the
migration.

**Note:**

Before upgrading production environments, create a database backup and review the release notes for migrations introduced
by the target version.

## Cluster Safety and Recovery

The migration framework is cluster-aware:

- Only one node can hold the migration lock and execute migrations.
- Lock ownership is refreshed while a migration is running.
- If a node stops while holding the lock, the lock expires and another node can continue.
- Migration runs without a `completedAt` timestamp are treated as aborted and can be recovered.
- If a node with an older database version leaves the cluster, the missing migrations are scheduled for execution.

This allows rolling deployments while preventing two nodes from applying the same migration at the same time.

## Get Migration Status

Use the migration status endpoint to list registered migrations and their run history.

**Option: CURL**

Endpoint: `/v1/admin/admin/migrations`

**Example Request**

```bash
curl -X 'GET' \
  'https://{host}/v1/admin/admin/migrations' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {superAdminToken}'
```

---

**Example Response**

```json
[
  {
    "version": 4,
    "allowedActions": [
      "APPLY",
      "RUN",
      "DRY_RUN",
      "VALIDATE_PRECONDITIONS"
    ],
    "description": "Migrate 'timestamp' (millis from epoch) field to 'created_at' and 'updated_at' (string ISO date)",
    "affectsCollections": [
      "organization_trees"
    ],
    "runs": [
      {
        "runId": "1a5a4a42-2a3f-48df-a571-8a4b68c9a941",
        "action": "APPLY",
        "remark": "Startup of Application",
        "startedAt": "2026-06-25T09:15:00.000Z",
        "completedAt": "2026-06-25T09:15:03.000Z",
        "totalItems": 1200,
        "processedItems": 1200,
        "success": true,
        "messages": [],
        "errorMessage": null
      }
    ]
  }
]
```

### Response Fields

- `version`: Numeric migration version.
- `allowedActions`: Actions that can be scheduled for this migration.
- `description`: Human-readable summary of what the migration does.
- `affectsCollections`: MongoDB collections touched by the migration.
- `runs`: Previous or currently running migration attempts.
- `runId`: Unique identifier for one migration run.
- `startedAt`: Time when execution started. If this is empty, the action is scheduled but not yet started.
- `completedAt`: Time when execution finished. If this is empty after `startedAt` is set, the run is still active or was
  interrupted.
- `totalItems` and `processedItems`: Progress counters for migrations that report item-level progress.
- `success`: `true` for successful runs, `false` for failed runs, and empty while the run is still pending or active.
- `messages`: Informational messages written by the migration.
- `errorMessage`: Error details for failed runs.

## Schedule a Migration Action

Migration actions are scheduled through the administration API. The endpoint returns `202 Accepted` after the action has
been recorded. Execution starts once a node acquires the database migration lock.

**Option: CURL**

Endpoint: `/v1/admin/admin/migrations/{dbVersion}`

**Example Request**

```bash
curl -X 'POST' \
  'https://{host}/v1/admin/admin/migrations/{dbVersion}' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {superAdminToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "action": "DRY_RUN",
  "remark": "Check migration impact before production upgrade"
}'
```

**Path Parameters**

- `dbVersion`: Version of the migration to schedule, for example `4`.

**Body Parameters**

- `action`: Migration action to schedule.
- `remark`: Optional note stored with the migration run history.

---

**Response**

- `202 Accepted` - The migration action was scheduled.

## Migration Actions

The following actions can appear in `allowedActions`:

- `APPLY`: Applies the migration and updates the cluster database version after success. This is the normal automatic
  startup/background action.
- `RUN`: Executes the migration logic without updating the cluster database version.
- `DRY_RUN`: Executes the migration's dry-run logic where supported. Use it to inspect impact before applying a migration.
- `VALIDATE_PRECONDITIONS`: Runs precondition checks and records messages or errors without applying the migration.
- `ROLLBACK`: Runs the rollback logic where supported. Not every migration supports rollback.

**Warning:**

Only schedule actions listed in `allowedActions` for the migration. Unsupported actions are rejected by the API.

## Dry Run and Rollback Support

Dry run and rollback support is defined per migration. Always check the `allowedActions` field returned by
`GET /v1/admin/admin/migrations` before scheduling either action.

### Dry Run

`DRY_RUN` is used to preview the impact of a migration without applying persistent changes. A dry run still creates a
migration run history entry, so you can inspect `messages`, `totalItems`, `processedItems`, `success`, and `errorMessage`
after it finishes.

Use dry runs when you want to:

- Check that migration preconditions pass.
- Estimate how many records are affected.
- Review informational messages before applying a data migration.

**Option: CURL**

Endpoint: `/v1/admin/admin/migrations/{dbVersion}`

**Example Request**

```bash
curl -X 'POST' \
  'https://{host}/v1/admin/admin/migrations/{dbVersion}' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {superAdminToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "action": "DRY_RUN",
  "remark": "Preview migration impact before applying it"
}'
```

### Rollback

`ROLLBACK` executes a migration's rollback logic where that migration supports it. Rollbacks do not update the cluster
database version. They are intended for targeted operational recovery, not as a replacement for a database backup.

**Warning:**

Not every migration can be rolled back. Data transformations may not have enough information to reconstruct the previous
state. For production upgrades, always keep a MongoDB backup even if a migration lists `ROLLBACK` in `allowedActions`.

**Option: CURL**

Endpoint: `/v1/admin/admin/migrations/{dbVersion}`

**Example Request**

```bash
curl -X 'POST' \
  'https://{host}/v1/admin/admin/migrations/{dbVersion}' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {superAdminToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "action": "ROLLBACK",
  "remark": "Rollback migration after operational validation"
}'
```

## Common Operational Flow

For a production upgrade:

1. Back up the MongoDB database.
2. Deploy the new Enterprise API version.
3. Watch the application logs for migration progress.
4. Call `GET /v1/admin/admin/migrations` to verify each required migration has `success: true`.
5. Investigate any run with `success: false` or an `errorMessage`.

For a manual check before applying a specific migration:

1. Call `GET /v1/admin/admin/migrations` and find the migration version.
2. Schedule `VALIDATE_PRECONDITIONS` if it is listed in `allowedActions`.
3. Schedule `DRY_RUN` if it is listed in `allowedActions`.
4. Review the `runs` entry for messages, progress, and errors.
5. Schedule `APPLY` only when you are ready to execute the migration.

## Current Migration Coverage

The Enterprise API currently registers migrations for:

- Initial MongoDB collection creation. This migration supports `DRY_RUN`.
- Base indexes for `organization_trees`, `host_aliases`, and `accounts`.
- Extended indexes for `organization_trees`.
- Event collection indexes.
- Migration of legacy `timestamp` fields to `createdAt` and `updatedAt` on `organization_trees`. This migration supports
  `DRY_RUN`.
- An `updatedAt` index for sorting and querying `organization_trees`. This migration supports `ROLLBACK`.

New Enterprise API releases may add more migrations. Always use the status endpoint as the source of truth for the
migrations available in your running version.

## Troubleshooting

### Migration Is Waiting

If a migration is scheduled but has no `startedAt`, another node may currently hold the migration lock. The scheduled run
will start when a node acquires the lock.

### Migration Was Interrupted

If a migration has `startedAt` but no `completedAt`, the node may have stopped before finishing. The framework treats this
as an aborted run and will attempt recovery on startup or when a migration recovery job is triggered.

### Migration Failed

If `success` is `false`, check `errorMessage` and the application logs for the root cause. After resolving the issue, you
can schedule the same supported action again.

### No Migrations Are Listed

If the status endpoint returns an empty list, verify that the deployment is using MongoDB as its database. Migration
execution is not supported for the in-memory database.
