Diffing & Pushing

Preview what a push would change with a read-only diff, then apply it to a target instance by choosing a push mode, passing the deletion gates, and resolving record identity.

With a snapshot pulled and committed, diff previews what applying it to a target would change, and push applies it. Both commands read the committed files: what you push is what is in git, not what is currently on the source instance.

Previewing with diff

d6s sync diff --to production

diff applies nothing. It previews the schema change, then has the target server dry-run the data import and roll it back, so the data plan is the server's own answer, not a client-side guess.

Every preview opens by naming the target instance and what the mode would mean (merge — additive, no deletions), then lists schema changes line by line and the data plan per collection (+N new ~N updated).

One thing a diff never does is guess: a committed record that could match more than one target record is reported as unresolved, not previewed as a create. An interactive push resolves it by asking you; a push in CI refuses. See record identity below.

Push modes

d6s sync push --to production                # merge (the default)
d6s sync push --to production --mode mirror  # deletes, behind the gates below
ModeSchemaDataDeletes?
addAdditiveInserts only; existing records are never updatedNo
merge (default)AdditiveCreates and updatesNo
mirrorMay delete in scopeCreates, updates, and deletes records absent from the filesYes, gated
Pull before a mirror push. The committed tree is what a push applies. A scoped pull refreshes only its scope, so the rest of the tree keeps whatever it last knew, and a mirror push applies that stale state to the target. Run a full pull first.

Deletion gates

Only mirror deletes, and deleting always requires its own explicit consent:

ContextTo delete you must
Interactive terminalReview the plan naming the losses, then type the profile name (unless you passed --dangerously-allow-delete, which is the consent)
Non-interactive / CIPass --dangerously-allow-delete

--yes skips the ordinary confirmation prompt, but it never authorizes a deletion. A mirror push in CI without --dangerously-allow-delete refuses before changing anything.

How a push runs

A push applies in two phases, schema first:

  1. Schema. The schema change applies first, sealed with a hash of the target's schema. If the target changed between planning and applying, the push stops instead of applying a stale plan.
  2. Data. Configuration records import once the schema is in place.

The two phases are not one transaction. If the data import fails after the schema applied, run the push again: the schema now matches, so the re-run applies data alone.

Record identity

The same role or flow carries different primary keys on each instance, so records have to be matched across them. Two mechanisms decide which target record a committed record is:

  • The committed identity map. Each push records source-to-target pairs in directus/<project>/id_map.json. Commit it; it is how the next push updates a record instead of creating a duplicate.
  • Natural keys. A record not yet in the map is matched to a target record by a natural key: its name for most resources, its email for a user, its key for an operation.

Ambiguity is never resolved by guessing. When two target records could both be the match, the CLI prompts you to choose in a terminal, and refuses in CI.

The first push into a target seeded from the same template can ask a few of these identity questions about pairs of records that plausibly are the same one. That is expected: answer them, the answers land in the identity map, and the questions do not come back. It converges in one pass.

Version matching

Schema changes require the snapshot's Directus version and the target's version to match exactly, patch release included. A mismatch refuses the command and names both versions. Align the instances (re-pull if the source was upgraded), or pass --allow-version-drift to override the gate, which proceeds with a loud warning. The CLI does not translate schema between versions.

Convergence

A push that applied cleanly leaves nothing behind:

d6s sync push --to production
# → schema and data match; nothing to push.

Re-running a completed push is safe, and the clean state is verified against the target, not assumed. If a connection drops mid-import and the result is unknown, the CLI says so. Run d6s sync diff before retrying rather than risking a blind retry.

Running diff and push unattended? CI & Automation covers non-interactive behavior, tokens, and JSON reports.

Get once-a-month release notes & real‑world code tips...no fluff. 🐰