On this page

As of the `4.x` series, Medplum supports a safe and well-defined rollback window for server deployments, provided you control when post-deploy migrations run.

note

Rolling back a Medplum server deployment has historically been unsafe, because post-deploy migrations can alter data in ways that are not reversible by simply deploying an older image. The `4.x` release process introduces a clearer separation between reversible and irreversible changes, enabling controlled rollbacks under specific conditions.

This page explains how to enable rollbacks by deferring post-deploy migrations with the `disablePostDeployMigrations` server config setting.

## When Rollbacks Are Safe

Since the `4.x` series, Medplum has structured its release process so that each minor version's post-deploy migrations are the only thing that breaks backward compatibility with the previous minor version's database state. In other words:

- The **pre-deploy** schema changes introduced by a new minor version are additive and tolerated by the previous minor version's code.
- The **post-deploy** migrations introduced by a new minor version are where irreversible data reshaping happens.

This means that if you deploy a new minor version but **do not run its post-deploy migrations**, you can still safely roll back to the latest patch of the **previous** minor version.

The rollback window

You can safely rollback to the **latest patch of the previous minor version** — and only the previous minor — as long as no post-deploy migrations for the new minor version have been applied.

### Example

Suppose your cluster is running `v4.2.6` and you want to upgrade to `v4.3.x`:

```text
v4.2.6 (latest patch of previous minor) ──┐
                                          │  ← safe rollback window
v4.3.15 deployed, post-deploy deferred ───┘
```

Once post-deploy migrations for `v4.3.15` have been applied, the rollback window closes. At that point you are fully committed to `v4.3.x` and later.

## How It Works

Medplum server has a config setting called `disablePostDeployMigrations`. When set to `true`, the server starts up and runs pre-deploy migrations normally, but does not automatically enqueue any post-deploy migrations.

| Setting Value | Behavior |
| --- | --- |
| `false` (default) | Post-deploy migrations run automatically in the background after server startup. |
| `true` | Post-deploy migrations are deferred and must be triggered manually by a Super Admin. |

By keeping `disablePostDeployMigrations` set to `true` during your upgrade, your new-minor-version server instances will be running the new code against a database that is still compatible with the previous minor version. That compatibility is what makes the rollback safe.

## Step-by-Step: Upgrading with Rollback Safety

### 1\. Set `disablePostDeployMigrations` to `true`

Before deploying the new minor version, set the following in your server config.

For JSON config:

```json
{
  "disablePostDeployMigrations": true
}
```

For AWS Parameter Store, create or update the parameter:

```text
/medplum/{environmentName}/disablePostDeployMigrations
```

with the value `true`.

For environment variables:

```bash
export MEDPLUM_DISABLE_POST_DEPLOY_MIGRATIONS=true
```

See [Setting Medplum Server Configuration](/content/docs/self-hosting/setting-configuration/index.html) for more details on each configuration mechanism.

### 2\. Deploy the New Minor Version

Deploy the latest patch of your target minor version (for example, `v4.3.x`) using your standard deployment process — typically `medplum aws update-server` for AWS deployments. See [Upgrading Medplum Server](/content/docs/self-hosting/upgrading-server/index.html) for the full upgrade procedure.

The server will start up, run pre-deploy migrations, and then **stop** without enqueueing any post-deploy migrations.

### 3\. Validate the Deployment

While `disablePostDeployMigrations` is `true`, the rollback window is still open. Use this time to:

- Run your smoke tests and integration tests against the new deployment
- Monitor application logs and metrics
- Verify that critical workflows behave as expected

If something goes wrong during this validation phase, you can roll back.

### 4\. Manually Apply Post-Deploy Migrations

Once you are confident in the new deployment, apply the post-deploy migrations manually. As a Super Admin, navigate to `/admin/super/asyncjob` in the Medplum app, or `POST` to the migration endpoint:

```bash
export AUTH_TOKEN=$(npx medplum token)

curl -X POST \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  https://api.example.com/admin/super/migrate
```

Monitor the resulting `AsyncJob` resource until all pending migrations report a completed status.

Rollback window closes here

Once post-deploy migrations for the new minor version begin to run, **the rollback window is closed**. You cannot safely roll back after this point.

### 5\. Re-enable Automatic Post-Deploy Migrations (Optional)

After you have committed to the new minor version, you may set `disablePostDeployMigrations` back to `false` so that future minor upgrades behave normally. If you plan to use this rollback pattern for every minor upgrade, you can leave it set to `true` permanently and continue applying post-deploy migrations manually after each validation window.

## Performing a Rollback

If validation reveals a problem and you need to roll back:

1. **Confirm no post-deploy migrations have run.** Query `AsyncJob` resources where `type` is `data-migration` and confirm there are no entries corresponding to the new minor version. If any have started — even partially — rollback is no longer safe; contact Medplum support.
2. **Deploy the previous minor version's latest patch.** For example, if you were rolling forward from `v4.2.6` to `v4.3.15` and need to revert, redeploy `v4.2.6` (or whatever is currently the latest patch of `v4.2.x`) using your standard deployment process.
3. **Leave `disablePostDeployMigrations` as is.** The previous minor version will ignore the setting for its own migrations (which have already run) and will start normally against the existing database.
4. **Verify that the rolled-back server is healthy** via your usual health checks and monitoring.

## Best Practices

- **Always pin to a specific patch version** — never use `:latest` — so the version the server actually starts with matches what you intended to deploy. 
- **Test the rollback procedure in staging** before you need it in production. A rollback you have never rehearsed is not a rollback plan.
- **Keep the validation window short.** The longer post-deploy migrations are deferred, the more unindexed / unbackfilled data accumulates, and the longer those migrations will take to complete when you do eventually run them.
- **Do not skip minor versions, even with this procedure.** The sequential-minor-version rule described in [Upgrading Medplum Server](/content/docs/self-hosting/upgrading-server/index.html) still applies.
