Secrets & Limitations

How Environment Sync keeps secret values out of the files you commit, the one exception to review before publishing a repository, and what the tool deliberately does not do.

How secrets are handled

A pull exports real records: the settings row, user accounts, flow definitions. If any of those carried a secret value, it would land in JSON files you commit, and git history keeps it forever, in every clone of the repository. Environment Sync strips secret values at export:

  • Built-in secret columns (password hashes, tokens, 2FA seeds, license and AI keys) are always deleted from the export.
  • Fields you created and marked concealed, hashed, or encrypted are deleted too. Every pull fetches the server's complete field list and drops any flagged value, printing a line naming each dropped field. A field missing from the export reads as protection, not data loss.

Because the field list is fetched separately from the schema, the check also covers pulls scoped to a few collections and pulls that skip schema entirely. And if the check itself cannot run, the pull stops rather than continue unprotected.

The field definition still syncs as schema: the column and its concealed setting arrive on the target. Only the value stays behind; set the real secret on each instance directly.

Why strip instead of sync

The server never hands out the real value for these fields: concealed fields read back as **********, hashed fields as the hash. Exporting that and pushing it would overwrite the target's working secret with a mask. Stripping protects both the repository and the target.

The one blind spot

Flow request headers export verbatim. A secret pasted into free-form flow configuration (most commonly an Authorization header in a request operation) has no "this is secret" marker for the CLI to check, and legitimate headers have to sync. The pull warns you by operation name, and the value goes into the committed file as-is. Review those files before committing, and before publishing a repository.

System collections that do not sync

None of these sync today. Some are shared configuration that a future release could take on; the rest are per-instance data that a sync should never touch.

CollectionWhy it doesn't sync
directus_presetsBookmarks, saved layouts, and Insights presets mix shared configuration with personal preference. A future release may sync the shared part.
directus_extensionsThe enabled/disabled state of installed extensions is tied to what is physically deployed on each instance. Deploy extensions to the target before pushing schema or flows that depend on them; the push itself does not warn.
directus_filesFile-interface fields sync as schema; file rows and the binaries behind them are their own workstream.
directus_commentsContent comments; per-instance data.
directus_activityThe audit log; per-instance data.
directus_revisionsChange history; per-instance data.
directus_versionsContent-versioning drafts; per-instance data.
directus_notificationsUser notifications; per-instance data.
directus_sharesPublic share links; per-instance data.
directus_sessionsActive login sessions; per-instance data.
directus_migrationsThe database migration ledger. The CLI never runs migrations or changes the Directus version.
directus_webhooksDeprecated in Directus; superseded by flows.

What it does not do

Won'tBecause / instead
Sync your collections' content (rows)Environment Sync moves the shape of a project and its configuration. Content sync is deferred to a future release.
Undo a pushRevert the commit and push again; removals need mirror. See Roll back a bad push. Only a database backup covers content.
Auto-expand a scoped snapshotA scope pulls exactly what you name. Dangling references produce a warning; widen --collections yourself.
Translate schema across versionsA version mismatch refuses the command; --allow-version-drift overrides the gate but converts nothing.
Wrap schema and data in one transactionSchema applies first, then data. A failed import re-runs data alone; see How It Works.
Select individual recordsResource selection is by type (--roles), never by row.
Model code-firstModel in the Data Studio and pull. Don't hand-author the JSON files.
Seal the data plan against target driftOnly the schema apply is sealed against the target changing between preview and apply. The data preview is the server's own dry-run answer, but it is advisory.

Known limitations

  • Translations mirror is opt-in. A server limitation currently breaks mirror pushes of translations, so translations are excluded from pulls by default. Opt in with --translations; merge and add pushes work normally.
  • Unlicensed custom permission rules are invisible to the export. On an instance without a license, the API hides custom permission rules, so a pull cannot export them. The pull detects the shortfall and marks the export incomplete: merge and add push normally, mirror refuses. License the source instance to export them.
  • Users without an email address fail import. The server's import validation rejects a user with no email, so a --users push containing one fails before any data applies. Give the account an email on the source, or remove it, first.
  • Panels can duplicate on a first sync into a look-alike target. Panels have no name to match on, so pushing into a target that already holds equivalent panels (seeded from the same template) can create them a second time. The identity map prevents repeats after that first push.
  • A mirror push of users does not protect your own account. If users are committed and the account the push authenticates with is absent from them, a mirror push orders its deletion like any other record; the CLI has no self-protection, and whether the server refuses is up to the server. When you sync users, make sure the committed set includes the accounts your pushes run as.

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