> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sqd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Schema changes and backfills

> Change schemas and backfill data safely on SQD Cloud.

A database migration changes the schema. It does not reconstruct historical values for a new column, table, or indexing rule.

Before deploying a schema change, decide whether the change needs:

* only a database migration;
* an application-level backfill from data already stored in Postgres; or
* a full blockchain replay in a fresh slot.

## Choose a strategy

| Change                                                        | Recommended strategy                   | Production impact                                                           |
| ------------------------------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------- |
| Add an index or constraint                                    | Apply an incremental migration         | Brief deployment restart; large indexes can still lock or load the database |
| Add a nullable column used only for new blocks                | Apply an incremental migration         | Existing rows remain `NULL`                                                 |
| Populate a new field from existing database columns           | Migration plus a separate backfill job | Keep the indexer running; control database load                             |
| Add a table or field that requires historical blockchain data | Deploy a fresh slot and replay         | Old slot stays live until cutover                                           |
| Change historical interpretation or processor filters         | Deploy a fresh slot and replay         | Safest way to validate complete output                                      |
| Repair a small, known range                                   | Build a purpose-specific repair job    | SQD Cloud does not provide a generic partial-history backfill switch        |

<Warning>
  SQD Cloud does not automatically backfill historical data after a schema or
  handler change. Applying a migration and redeploying the existing slot only
  affects future processing unless your code explicitly repairs old rows.
</Warning>

## Option 1: Update the existing slot

Use an in-place update when the existing database is valid and the change is additive or operational.

1. Add and test an incremental migration locally.

2. Back up any non-reconstructable data.

3. Deploy to the existing slot:

   ```bash theme={"system"}
   sqd deploy . -n <name> -s <slot>
   ```

4. Confirm that the [`init` service](/en/cloud/reference/manifest#init) applies the migration successfully.

5. Check processor logs, API queries, and indexing progress.

Do not use `--hard-reset`: it drops and recreates all deployment resources, including the database.

## Option 2: Backfill from existing Postgres data

If the new value can be derived from existing tables, run the backfill outside the processor transaction:

1. Apply the additive schema migration.
2. Deploy a separate [background job](/en/cloud/resources/background-jobs).
3. Update rows in bounded batches.
4. Store progress so the backfill can resume after a restart.
5. Monitor locks, statement duration, and database growth.
6. Remove the temporary job when verification is complete.

Avoid one unbounded `UPDATE` over a large table. It can create long locks, large transactions, and table bloat.

## Option 3: Replay in a fresh slot

Use a new slot when correctness depends on replaying historical blocks:

1. Keep the current slot tagged `production`.

2. Deploy the changed code without specifying a slot or tag:

   ```bash theme={"system"}
   sqd deploy .
   ```

3. Wait for the new slot to reach the expected chain height.

4. Validate row counts, representative queries, and application behavior through the slot URL.

5. Move the production tag:

   ```bash theme={"system"}
   sqd tags add production -n <name> -s <new-slot>
   ```

6. Keep the old slot temporarily for rollback.

7. Remove the old slot only after the observation window:

   ```bash theme={"system"}
   sqd remove -n <name> -s <old-slot>
   ```

See [Slots and tags](/en/cloud/resources/slots-and-tags#zero-downtime-updates) for the complete cutover and rollback procedure.

<Info>
  Parallel slots are independent deployments and are billed independently while
  both are running. Estimate the overlap before starting a long replay.
</Info>

## Validate before cutover

At minimum, compare:

* processor height and lag;
* entity counts for important tables;
* known records at old, middle, and recent blocks;
* API response shape and latency;
* custom aggregate or materialized-table output;
* error and restart rates.

If the new schema removes or renames API fields, treat the deployment as an API version change. Use a separate tag such as `production-v2` until clients have migrated.

## Roll back

If validation fails after moving the tag:

1. Move `production` back to the old slot.
2. Stop sending traffic to the new slot.
3. Inspect its logs and database without changing the old slot.
4. Fix the issue and redeploy to the same new slot, or remove it and start another replay.

Moving a tag changes the stable API destination. It does not merge, copy, or roll back database contents between slots.

## Data that cannot be recreated

Managed Postgres should not be the only copy of user-generated or externally sourced data that an indexer replay cannot reproduce. Store that data separately or arrange the appropriate backup and availability requirements before destructive schema work.
