Product

|

Building Environment Sync in Directus: our considerations, requirements and choices

Dive deeper into the Environment Sync development choices including our consideration, feedback received and how it shaped our reasoning.

Bryant Gillespie

Staff Product Engineer

You've got three things in dev. The new posts collection is done and QA signed off. A half-built dashboard someone's still poking at. And a flow that currently sends every form submission to a webhook endpoint that doesn't exist yet.

Marketing wants the posts collection in production this afternoon. So naturally you take the posts collection to production this afternoon:

d6s sync pull --from dev --collections posts --no-flows --no-dashboards
d6s sync diff --to production
d6s sync push --to production --yes
d6s sync pull --from dev --collections posts --no-flows --no-dashboards
d6s sync diff --to production
d6s sync push --to production --yes
d6s sync pull --from dev --collections posts --no-flows --no-dashboards
d6s sync diff --to production
d6s sync push --to production --yes
d6s sync pull --from dev --collections posts --no-flows --no-dashboards
d6s sync diff --to production
d6s sync push --to production --yes

The dashboard and the flow stay exactly where they are. That's Environment Sync, and it's the thing this space has been asking for: a unit of change smaller than your entire project, moving through files you can commit, diff, and review.

We’re really excited to share it and wanted to talk through the shape we landed on.

Teams choose Directus because modeling schema and building APIs is fast. You can see your data model in real time, iterate with non-developers in the room, and get to something working in hours, instead of days. For simple projects, that's all you ever need.

But as projects grow in maturity - more environments, more contributors, more process - teams start to want things that have traditionally lived in code-based workflows: version control, peer review, an auditable history of what changed and when.

Until now, there hasn't been a great way to have both. Environment Sync changes that. You keep modeling in the UI, exactly as you do today. And your configuration can now be exported as files that you can commit, diff, review, and apply to other environments.

Model your schema, access policies, and more in the UI (or with AI) and export to files, or start from files and apply them to a fresh instance — the configuration is portable in both directions. Either way, the files give you governance, an audit trail, and a repeatable baseline.

If you have followed this space for a while, you may have seen a few different phrases used to describe a similar set of needs: config-as-code, code-first schema management, and environment sync.

The terms overlap, but they do not always mean the same thing. Using the wrong label for the solution can make it sound like we are solving one problem while actually delivering another.

But when we dug into the research - customer calls, community discussions, forum threads - what stood out was that beneath the different labels, the underlying problem was usually the same: reliably moving changes between environments.

And the existing tools made that harder than it should be. Schema exports were large, monolithic JSON/YAML blobs - verbose, intimidating, and difficult to interpret or git diff effectively. Whatever term people reached for, what they needed was a practical way to sync environments: store configuration in files, promote changes selectively, and use those files in source-controlled workflows. That is why the current work is best understood as environment sync.

The practical problem behind the request

The real challenge here underneath the all-or-nothing workflow is that a feature or change happening in dev may be ready, when another is still being built out. With all-or-nothing, you can’t take what’s ready without bringing the other (incomplete) change with you - that’s a real software development lifecycle (SDLC) problem.

Asking for a better way to manage configuration across environments, much of that need was fundamentally about portability, promotion, and synchronization. The solution being delivered addresses those needs in a way that is aligned with Directus as it exists today: UI-first, collaborative, and practical for real-world deployment workflows.

The solution discussed here is centered on two pieces. First, API changes allow teams to apply a specific subset of resources instead of relying on a single monolithic schema. Second, a CLI is intended to make that workflow usable in the way teams actually work, including more structured promotion and deployment flows.

The broader shape is a file-based workflow: configuration can be exported, stored, committed, and applied to other instances. In practice, that means teams can work with a more targeted unit of change: specific resources, specific updates, and a workflow that better reflects how real projects evolve.

Alongside that, the intention is to support a file-based workflow where configuration can be exported, stored, committed, and applied elsewhere. The CLI plays an important role here, because it is what makes the workflow practical in day-to-day engineering and deployment processes.

Why code-first isn’t a perfect fit for Directus

Environment Sync isn't a code-first system. You don't define collections in TypeScript, and code isn't the source of truth. That's worth explaining rather than glossing over, because "just let us define schema in code" is a reasonable thing to want and we thought hard about it.

Every schema workflow encodes an answer to one question: who's allowed to change the model?

Code-first answers it clearly, and the answer is people who can open a pull request. For a lot of teams that's correct. It's wrong for what Directus is good at.

Think about who touches the model on a healthy Directus project. The content lead who added three fields when the editorial workflow changed. The ops person who built the dashboard the warehouse team lives in. Neither of them is opening a PR. Under code-first, both of those become tickets in someone's sprint, and then the predictable thing happens: when changing the model gets expensive, people stop changing it. You get the catch-all JSON field and the "notes" column with six competing conventions in it. The schema quietly stops describing the business.

Developers don't come out ahead either. Code-first doesn't remove the collaboration, it just makes you the field-adding service desk.

Here's the reframe that decided it. Code-first puts review at the authoring boundary, where nothing exists until someone approves the code. Environment Sync puts it at the promotion boundary instead. Anyone can model in dev, and nothing reaches production without a diff someone signed off on. You don't need a pull request to add a field in dev. You need one before that field hits production. The shape fits the rest of the product too.

Directus starts from a database, not from a blank file

Directus points at databases that already exist, so making code the source of truth would hand every existing project a reverse-engineering migration on day one. And schema is the small part of this: roles, permissions, dashboards, panels, and translations have no code representation today, so "define it in code" isn't a workflow change, it's an authoring surface that has to be designed and built for each one first. Some of that is worth wanting, and plenty of people would happily define a flow in TypeScript. It's just a much bigger project than an export format, and it isn't what's blocking anyone this week.

One more thing we didn't expect to matter as much as it does: files pulled from a live instance are accurate by construction. Code definitions describe what a database should be, and drift stays invisible until something breaks. diff answers "are these two environments actually the same right now" against the running server, which is the question most teams were really asking.

The door isn't closed on a code-first path. It just wasn't the thing blocking anyone.

Community shaped the result

The Directus community got here first. directus-sync is the most established extension example - it provides a solid foundation for syncing Directus configuration between environments. Much of the design thinking throughout the RFC and building was shaped with the established patterns from it’s widespread use in mind. We want to acknowledge that work explicitly and thank EdouardDem and all the contributors first for building a wonderful community contribution.

Next, there’s also a huge thank you needed for everyone who took the time to share their thoughts in the discussion, on the roadmap and in the RFC. Specific requests influenced the design, including functionality around deleting configuration resources, which was added behind a clear and explicit flag. This was not built in a vacuum, and whilst we couldn’t accomplish absolutely everything requested in this release, it has been shaped through direct engagement with the people who have been asking for better workflows in this area and we thank you for making it better.

There's more to do here, and we want to hear what you hit. The forum and the roadmap are both open.