---
title: "10: Sync vs rollouts | SurrealDB University"
description: "Why sync belongs on disposable databases and how SurrealKit rollouts ship catalog changes safely."
url: https://surrealdb.com/learn/schemas/page-10
---

![Course content preview](https://surrealdb.com/assets/static/course-schemas.D4CFbBhP.avif)

[Back to Courses](https://surrealdb.com/learn)

Course chapters

[Schema internals and migrations](https://surrealdb.com/learn/schemas) Internals [1: Schemaless vs. schemafull](https://surrealdb.com/learn/schemas/page-01) [2: Schema internals](https://surrealdb.com/learn/schemas/page-02) [3: Migrations](https://surrealdb.com/learn/schemas/page-03) [4: Data types](https://surrealdb.com/learn/schemas/page-04) [5: Automation](https://surrealdb.com/learn/schemas/page-05) Migrations [6: SurrealKit and the first table](https://surrealdb.com/learn/schemas/page-06) [7: Activities and seed data](https://surrealdb.com/learn/schemas/page-07) [8: Computed and asserted fields](https://surrealdb.com/learn/schemas/page-08) [9: Graph dependencies](https://surrealdb.com/learn/schemas/page-09) [10: Sync vs rollouts](https://surrealdb.com/learn/schemas/page-10) [11: Milestones](https://surrealdb.com/learn/schemas/page-11) [12: People and indexes](https://surrealdb.com/learn/schemas/page-12) [13: Events and CI](https://surrealdb.com/learn/schemas/page-13) [14: Capstone](https://surrealdb.com/learn/schemas/page-14)

# 10: Sync vs rollouts

In parts [6](https://surrealdb.com/learn/schemas/page-06) to [9](https://surrealdb.com/learn/schemas/page-09) you got used to a simple loop: edit a file under `database/schema/`, then run `surrealkit sync` (or leave `sync --watch` running). That loop is still the right one for your laptop and any other database you are happy to wipe.

This chapter is about the other SurrealKit path: `rollouts`. We will look at how they work, try a dry-run and a harmless `plan`, and at the end create a schema file that we deliberately leave unapplied. The [next chapter](https://surrealdb.com/learn/schemas/page-11) is where that file finally goes into the database.

## Two jobs, two commands

Here is the short version of the difference:

|  | `sync` | `rollout` |
| --- | --- | --- |
| Database | Local / disposable (CI ephemeral DBs count) | Shared staging or production |
| Feel | Make the live catalog match the files now | Plan a reviewed change, apply in phases |
| Deletes | Removing a `DEFINE` from a file can `REMOVE` it on the next sync | Removals wait for an explicit `complete` phase |
| Who runs apply | You, against your `.env` | Usually CI / CD with shared secrets |

Same connection flags (`--user`, `--pass`, `--ns`, `--db`). Different database, different risk. The docs page [sync vs rollouts](https://surrealdb.com/docs/manage/schema-migration/getting-started/sync-vs-rollouts) says the same thing at more length.

If you only ever use a personal `memory` database or a throwaway file, you can live on `sync` for a long time. The moment other people or services share the same namespace, everyday `sync` becomes a bad idea.

At this point you may be tempted to use rollouts even for a personal project due to the ability to plan changes before applying. However, you can still stick with regular `sync` thanks to the `--dry-run` flag which lets you preview changes before applying them.

## Preview a sync with `--dry-run`

Being cautious on a database you own doesn't mean that a full rollout is required. `sync --watch` already applies on each save, so it is not the place for a preview. Use dry-run when you are about to run a one-shot sync (for example after you stop watch with Ctrl+C, or when you deleted or renamed a `DEFINE` and want to see the prunes before they land):

```bash
surrealkit sync --dry-run --user root --pass secret --ns main --db main
```

You will see which schema files would be applied. That is usually all you get on an additive change (a new table, a tightened field, and so on). SurrealKit only prints `REMOVE` statements when something is stale: a definition that is still tracked in `__entity` but no longer present in your schema files. The catalog itself stays unchanged either way.

Dry-run is still the sync worldview: make the database match the files. It is not a substitute for expand → deploy → contract on a shared host. For the full list of sync flags, see [Sync](https://surrealdb.com/docs/manage/schema-migration/sync).

## Expand, then contract

Shared databases need a window where old application code and new application code can both work:

1. Expand: add new tables, fields, indexes (nothing destructive yet)
2. Deploy: ship the app that uses those definitions
3. Contract: remove the old definitions the previous app no longer needs

Rollouts turn that into commands:

| Step | Command (shape) | What it does |
| --- | --- | --- |
| Plan | `surrealkit rollout plan --name …` | Diff schema files vs snapshots; write a `manifest` under `database/rollouts/` |
| Start | `surrealkit rollout start <manifest-id> …` | Apply the expand phase; record progress in `__rollout` |
| Complete | `surrealkit rollout complete <manifest-id> …` | Apply the contract phase (`REMOVE`s, dropped indexes, and so on) |
| Rollback | `surrealkit rollout rollback <manifest-id> …` | Undo the `start` (expand) phase while the rollout is still in flight |

And here is the same idea as a small map.

```text
plan  (manifest on disk; shared DB unchanged)
  │
  │   Stop here if the plan looks wrong: edit schema, re-plan,
  │   or discard the .toml. No `rollback` needed; nothing was applied.
  ▼
start  (expand applied → status ready_to_complete)
  │
  ├──────────────────────────────┐
  │                              │
  ▼                              ▼
deploy app                    rollback
  │                           (undo expand)
  ▼                              │
complete  (contract applied)     ▼
  │                           rolled_back
  ▼                           (terminal)
completed
(terminal; `rollback` will refuse)
```

After `plan` alone you are ready to start. After `start` you can roll back or finish. And after `complete`, `rollback` will refuse. The docs have the full [state machine](https://surrealdb.com/docs/manage/schema-migration/rollouts#rollout-state-machine) if you want every status name.

You can plan on your laptop. `start` and `complete` against a shared database should use credentials that are not sitting in the same `.env` you use for local sync (more on that in part [14](https://surrealdb.com/learn/schemas/page-14)).

## What a manifest is

`surrealkit rollout plan --name add_milestones` does not invent an id out of thin air. It writes a file that looks like this:

```text
database/rollouts/20260302153045__add_milestones.toml
```

The `manifest id` is that filename without `.toml`:

```text
20260302153045__add_milestones
```

The digits at the front are a timestamp. The bit after `__` comes from `--name`. Yours will not match the example. After you plan, list the folder:

Bash

PowerShell

```bash
ls database/rollouts/
```

Then use that stem in later commands (still from the SurrealKit project root: the folder that contains `database/`, not from inside `rollouts/`):

```bash
surrealkit rollout start 20260302153045__add_milestones --user root --pass secret --ns main --db main
surrealkit rollout complete 20260302153045__add_milestones --user root --pass secret --ns main --db main
```

On a real team change, commit the manifest (and any refreshed snapshots) with the schema so reviewers can see the intended apply, not only the `.surql` files.

Handy extras that do not mutate a shared DB:

```bash
surrealkit rollout lint 20260302153045__add_milestones
surrealkit rollout status
```

## Try `plan` locally

You do not need production for this. From your course SurrealKit folder (schema already synced through part 9):

Bash

PowerShell

```bash
surrealkit rollout plan --name course_probe
ls database/rollouts/
```

Open the new `.toml`. There are two items worth noting when you do so:

First, this plan is not empty. It lists **every** schema file you have written so far under an `apply_expand_schema` step:

```toml
[[steps]]
id = "apply_expand_schema"
phase = "start"
kind = "apply_files"
files = [
    "database/schema/activity.surql",
    "database/schema/activity_of.surql",
    "database/schema/depends_on.surql",
    "database/schema/project.surql",
]
```

That is because `plan` diffs your files against `database/snapshots/`, and `sync` never writes snapshots. Parts 6 to 9 filled the database but left `snapshots/` empty, so the first plan sees "nothing recorded" against "four files" and proposes the lot. Part [14](https://surrealdb.com/learn/schemas/page-14) covers `rollout baseline`, the command that records an agreed starting point on a database that already exists so a first plan comes back empty instead.

Second, the phases are `start` and `rollback`, not `start` and `complete`. A `complete` phase only appears when the plan contains something destructive, but since there is nothing to remove here, there is nothing to contract. The `rollback` step is the mirror of `start`, and on this particular manifest it would remove every table and field in the project. Be sure not to run `rollout start` on this probe.

The useful side effect is that `plan` writes `database/snapshots/` on its way out:

```text
Generated rollout manifest ./database/rollouts/20260810031409__course_probe.toml
Updated ./database/snapshots/catalog_snapshot.json
```

So this throwaway probe doubles as the course's baseline. That matters for the next chapter: because the snapshots now match what is live, part [11](https://surrealdb.com/learn/schemas/page-11)'s plan comes back containing `milestone.surql` and nothing else. Skip this section and part 11's manifest will list all five files instead.

The next section adds a schema file on disk only. Leave it unapplied until part [11](https://surrealdb.com/learn/schemas/page-11).

Note

Do not point everyday `sync` at staging or production just because the flags look the same. Sync can prune. Rollouts keep destructive work behind `complete` and a reviewed manifest. Until CI owns those secrets, keep shared hosts out of the `.env` you use for local sync (part [6](https://surrealdb.com/learn/schemas/page-06)).

## Park the next schema file

Until now, each new `.surql` file got synced as soon as you wrote it. Since we are nearing the end of this chapter we will simply create the file to get a taste of the next changes to apply and leave the database alone.

Create `database/schema/milestone.surql`:

```surql
DEFINE TABLE milestone SCHEMAFULL;
DEFINE FIELD project ON milestone TYPE record<project>;
DEFINE FIELD activities ON milestone TYPE array<record<activity>>;
DEFINE FIELD name ON milestone TYPE string;
DEFINE FIELD last_updated ON milestone VALUE time::now();
DEFINE FIELD progress ON milestone COMPUTED math::mean(activities.progress);
DEFINE FIELD is_complete ON milestone COMPUTED activities.all(|$a| $a.progress > 0.95);
```

Save it. Do not sync, dry-run, or start a rollout yet. Part [11](https://surrealdb.com/learn/schemas/page-11) will explain the fields, preview with `sync --dry-run`, then apply once with a rollout.

If `sync --watch` is still running from earlier, stop it with Ctrl+C first. A save under watch would apply `milestone` immediately and spoil the exercise.

## Checkpoint

- `sync` = disposable DB, immediate apply (including prune)
- `sync --dry-run` = same plan, no writes; useful when you are unsure about a prune
- `rollout` = shared DB, plan → start → deploy → complete (`rollback` only after `start`)
- `plan` diffs schema files against `database/snapshots/`, not against the live database, and refreshes those snapshots as it goes
- Manifest id = stem of `database/rollouts/<timestamp>__<name>.toml` (no `.toml` on the command line)
- `milestone.surql` on disk, not yet applied

Previous

9: Graph dependencies

[Previous](https://surrealdb.com/learn/schemas/page-09)

Next lesson

11: Milestones

[Next lesson](https://surrealdb.com/learn/schemas/page-11)

```json
{"@context":"https://schema.org","@type":"Course","name":"Schema internals and migrations","description":"Learn how SurrealDB stores schema metadata, how DEFINE statements shape your database, and how to migrate production data safely.","url":"https://surrealdb.com/learn/schemas","inLanguage":"en","isAccessibleForFree":true,"provider":{"@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com"},"hasPart":[{"@type":"LearningResource","name":"Schema internals and migrations","url":"https://surrealdb.com/learn/schemas"},{"@type":"LearningResource","name":"1: Schemaless vs. schemafull","url":"https://surrealdb.com/learn/schemas/page-01"},{"@type":"LearningResource","name":"2: Schema internals","url":"https://surrealdb.com/learn/schemas/page-02"},{"@type":"LearningResource","name":"3: Migrations","url":"https://surrealdb.com/learn/schemas/page-03"},{"@type":"LearningResource","name":"4: Data types","url":"https://surrealdb.com/learn/schemas/page-04"},{"@type":"LearningResource","name":"5: Automation","url":"https://surrealdb.com/learn/schemas/page-05"},{"@type":"LearningResource","name":"6: SurrealKit and the first table","url":"https://surrealdb.com/learn/schemas/page-06"},{"@type":"LearningResource","name":"7: Activities and seed data","url":"https://surrealdb.com/learn/schemas/page-07"},{"@type":"LearningResource","name":"8: Computed and asserted fields","url":"https://surrealdb.com/learn/schemas/page-08"},{"@type":"LearningResource","name":"9: Graph dependencies","url":"https://surrealdb.com/learn/schemas/page-09"},{"@type":"LearningResource","name":"10: Sync vs rollouts","url":"https://surrealdb.com/learn/schemas/page-10"},{"@type":"LearningResource","name":"11: Milestones","url":"https://surrealdb.com/learn/schemas/page-11"},{"@type":"LearningResource","name":"12: People and indexes","url":"https://surrealdb.com/learn/schemas/page-12"},{"@type":"LearningResource","name":"13: Events and CI","url":"https://surrealdb.com/learn/schemas/page-13"},{"@type":"LearningResource","name":"14: Capstone","url":"https://surrealdb.com/learn/schemas/page-14"}]}
```

```json
{"@context":"https://schema.org","@type":"LearningResource","name":"10: Sync vs rollouts","description":"Why sync belongs on disposable databases and how SurrealKit rollouts ship catalog changes safely.","url":"https://surrealdb.com/learn/schemas/page-10","learningResourceType":"lesson","isPartOf":{"@type":"Course","name":"Schema internals and migrations","url":"https://surrealdb.com/learn/schemas"},"position":13}
```

```json
{"@context":"https://schema.org","@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com","logo":"https://surrealdb.com/assets/static/logo.BG7_TG2b.svg","description":"SurrealDB is the unified data layer for AI. A multi-model database for documents, graphs, vectors, and time-series.","foundingDate":"2022","legalName":"SurrealDB Ltd","identifier":{"@type":"PropertyValue","propertyID":"GB-COH","value":"13615201"},"address":{"@type":"PostalAddress","streetAddress":"3rd Floor, 1 Ashley Road","addressLocality":"Altrincham","addressRegion":"Cheshire","postalCode":"WA14 2DT","addressCountry":"GB"},"contactPoint":[{"@type":"ContactPoint","contactType":"customer support","email":"support@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"sales","email":"info@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"security","email":"security@surrealdb.com","url":"https://surrealdb.com/.well-known/security.txt","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"legal","email":"legal@surrealdb.com","url":"https://surrealdb.com/legal","availableLanguage":"English"}],"hasCertification":[{"@type":"Certification","name":"SOC 2 Type 2"},{"@type":"Certification","name":"GDPR"},{"@type":"Certification","name":"Cyber Essentials Plus"},{"@type":"Certification","name":"ISO 27001"}],"owns":[{"@type":"SoftwareApplication","name":"SurrealDB","url":"https://surrealdb.com/surrealdb"},{"@type":"SoftwareApplication","name":"Agent Memory","url":"https://surrealdb.com/agent-memory"}],"knowsAbout":["multi-model databases","document databases","graph databases","vector search","time-series databases","SurrealQL","Agent Memory","real-time databases","embedded databases","context layer","graph ontology","distributed database","knowledge graphs","distributed transaction protocols","highly-scalable databases"],"sameAs":["https://www.wikidata.org/wiki/Q124316308","https://github.com/surrealdb/surrealdb","https://twitter.com/surrealdb","https://www.youtube.com/@surrealdb","https://www.linkedin.com/company/surrealdb","https://discord.gg/surrealdb","https://www.reddit.com/r/surrealdb","https://www.instagram.com/surrealdb","https://medium.com/surrealdb","https://dev.to/surrealdb"]}
```

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://surrealdb.com"},{"@type":"ListItem","position":2,"name":"Learn","item":"https://surrealdb.com/learn"},{"@type":"ListItem","position":3,"name":"Page 10","item":"https://surrealdb.com/learn/schemas/page-10"}]}
```
