Secrets & Limitations
How secrets are handled
A pull writes real configuration records to sync files: settings, user accounts, flow definitions, and more. If any carried a secret value, it would land in JSON files you commit, and git history would keep it in every clone of the repository. Environment Sync strips secret values during pull:
- Built-in secret fields (password hashes, tokens, 2FA seeds, license and AI keys) are always removed from the sync files.
- Fields you created and marked concealed, hashed, or encrypted are removed too. Every pull fetches the server's complete field list and drops any flagged value, printing a line naming each dropped field. A value missing from a sync file is protection, not data loss.
Because the field list is fetched separately from Schema, the check also covers pulls scoped to a few collections and pulls that skip Schema entirely. If the check itself cannot run, the pull stops rather than continue unprotected.
The field definition still syncs as Schema: the field 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. Writing that value to a sync file 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
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 state that a sync should never touch.
| Collection | Why it doesn't sync |
|---|---|
directus_presets | Bookmarks, saved layouts, and Insights presets mix shared configuration with personal preference. A future release may sync the shared part. |
directus_extensions | The 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_files | File-interface fields sync as Schema; file records and the binaries behind them are their own workstream. |
directus_comments | Content comments; per-instance state. |
directus_activity | The audit log; per-instance state. |
directus_revisions | Change history; per-instance state. |
directus_versions | Content-versioning drafts; per-instance state. |
directus_notifications | User notifications; per-instance state. |
directus_shares | Public share links; per-instance state. |
directus_sessions | Active login sessions; per-instance state. |
directus_migrations | The database migration ledger. The CLI never runs migrations or changes the Directus version. |
directus_webhooks | Deprecated in Directus; superseded by flows. |
What it does not do
| Won't | Because / instead |
|---|---|
| Sync records in your own collections | Environment Sync moves the shape of a project and its configuration. Content sync is deferred to a future release. |
| Undo a push | Revert the commit and push again; removals need mirror. See Roll back a bad push. Only a database backup covers content. |
| Auto-expand a scoped pull | A scope pulls exactly what you name. Dangling references produce a warning; widen --collections yourself. |
| Translate schema across versions | A version mismatch refuses the command; --allow-version-drift overrides the gate but converts nothing. |
| Wrap Schema and Configuration in one transaction | Schema applies first, then Configuration. A failed import re-runs Configuration alone; see How It Works. |
| Select individual records | Resource selection is by type (--roles), never by record. |
| Model code-first | Model in the Data Studio and pull. Don't hand-author the JSON files. |
| Seal the Configuration plan against target drift | Only the Schema apply is sealed against the target changing between preview and apply. The Configuration preview is the server's own dry-run answer, but it is advisory. |
Known limitations
- Unlicensed custom permission rules are unavailable to pull. On an instance without a license, the API hides custom permission rules. The pull detects the missing records and marks the result incomplete:
mergeandaddpush normally, whilemirrorrefuses. License the source instance to include them. - Users without an email address fail import. The server's import validation rejects a user with no email, so a
--userspush containing one fails before any Configuration changes apply. 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 ID map prevents repeats after that first push.
- A mirror push of users does not protect your own account. If users are in the sync files and the account the push authenticates with is absent from them, a
mirrorpush 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 sync files include the accounts your pushes run as.
Get once-a-month release notes & real‑world code tips...no fluff. 🐰
CI & Automation
Post the production diff on every pull request and push on merge, with tokens from environment variables, machine-readable JSON reports, and explicit flags in place of prompts.
Reference
Every Environment Sync command and flag, the directus.config.json format, credential resolution, scope tables, push modes, deletion gates, and JSON report formats.