CMS Environments, Development, and Content Workflow Best Practices

Agility CMS is flexible by design. Your content is decoupled from your presentation layer, so you can manage authoring workflow and code deployment as two independent concerns. This guide explains how to set up CMS environments, website deployments, and content workflows that match the size and complexity of your team.

Two short framing sections before the workflow guidance. The first clarifies what an Agility instance actually is and clears up the most common multi-site confusion we hear from prospects. The second explains why content environments do not need to mirror DevOps environments, which is the most common source of overengineering we see in customer architectures.

What is an Agility instance?

An Agility instance is a fully isolated CMS environment with its own content, models, users, API keys, sitemaps, channels, and configuration. Each instance has a unique GUID. When you buy Agility, you buy capacity in instances.

Customers typically arrive with two questions tangled together:

  • How many websites or apps will my Agility instance support?
  • How many Agility instances do I need?

These are different questions with different answers, and untangling them is the first job of this guide.

One instance can serve many web properties. A single Agility instance can power multiple websites, mobile apps, kiosks, in-product help, and other digital surfaces. Properties share content models and content where it makes sense, and use sitemaps and channels to give each property its own page structure, navigation, and routing. Most multi-site customers do this in one instance. See Using Agility CMS for Multiple Sites for the patterns.

There are real cases where separate instances per property are the right answer. Multi-tenant solutions where each customer needs a walled-off content surface. Distinct brands inside a holding company with no shared editorial team or governance. Recently-acquired properties being migrated separately. Brand-specific compliance requirements. These are exceptions and you will know if you are one of them.

The number of instances is a separate decision from the number of properties. That decision is about environments (sandbox, UAT, prod), not about how many sites or apps you are delivering. The rest of this guide answers that second question. Most customers need one instance.

Content environments are not DevOps environments

Content is not a database that gets mirrored across environments. It is a publishing pipeline with editorial states. A single CMS instance with a strong workflow is the right answer for marketing, even when their developer counterparts run a multi-environment DevOps pipeline for the website that consumes that content.

Developers come from a world where DEV, UAT, and PROD are mirrored. Schema migrates upward. Fixture data lives in lower environments. They project that pattern onto content. The projection is wrong, because the editorial team is the user of the CMS, and editors do not want three of them. They want one source of truth with a workflow on top.

Editorial workflow already handles the gating that DevOps environments solve for code:

  • Staging/Draft is the equivalent of working on a feature branch
  • Awaiting Approval is the equivalent of a pull request open for review
  • Approved is the equivalent of a merged change ready to ship
  • Published is the equivalent of a production deploy

All of this lives in a single Agility instance. You do not need to add CMS environments to get these states. You already have them.

What does mirror your DevOps environments is your website code. Your local, dev-test, QA, UAT, and production website builds all point at the same Agility instance. Each deployment can serve content in two modes: preview (returning Staging/Draft, Awaiting Approval, Approved, and Published items) and live (returning Published items only). Local development typically runs preview-only because that is what developers need. Every other deployment registered in Web Studio supports both modes. Editors copy the preview URL for any registered deployment when they want to validate unpublished content, including on production itself. End users see the live URL of production by default. That is the integration point, and it is sufficient for almost every customer.

If you take one thing from this guide, take this: do not add CMS environments to mirror your DevOps environments. Add them only when your specific scenario requires it. The rest of this guide explains what those scenarios look like.

Sitemaps and channels are not environments either

A related confusion worth calling out separately. A sitemap in Agility is the navigation and routing structure for one digital property. Channels group sitemaps under a single instance for multi-property setups. They exist so one instance can serve multiple websites or apps. They are not a substitute for DevOps environments, and they are not a workflow tool.

The pattern we see fail: a customer creates one sitemap for "production content," another for "staging content," another for "dev content," and another for "QA content." Editors are then asked to maintain four parallel copies of every page. Every content change has to be duplicated four times. Every release means manually reconciling four sitemaps. This is four times the editorial work and zero of the safety, because nothing prevents the wrong sitemap from going live, and there is no audit trail for which copy is canonical.

This usually arrives from teams whose previous platform forced them to model environments inside the content tree because it had no other way to do it. Agility does not work that way. The right tools for what those customers actually need are already in the box:

  • For non-production content surfaces that editors and reviewers can validate against, use the editorial workflow states (Staging/Draft, Awaiting Approval, Approved) and the preview URLs of your registered deployments. See the single instance workflow below.
  • For non-production environments justified by compliance or external QA fixtures, use the multi-environment topology with separate instances. See that section below.
  • For multiple websites or apps sharing one editorial team, use channels and sitemaps as designed: one per property, not one per environment.

If you find yourself asking editors to duplicate pages across sitemaps, stop. The answer is one of the three options above, not a fifth sitemap.

Model changes and the code that uses them

When you change a content or component model, the code that reads that model usually has to change too. This relationship is the real pressure behind most requests for a sandbox or multi-environment setup. Worth understanding before you decide your topology, because most model changes are safer than they look.

Three cases, in order of risk.

Additive changes are the easy case. Add a new field to an existing model and your existing code keeps working, as long as it does not choke on fields it does not recognize. Roll out the model change first. Roll out the code that reads the new field whenever it is ready. The two are decoupled. No environment separation required.

Removals are a two-step deprecation, not a one-step delete. Hide the field in the model and set it to not required. The code can stop using it whenever. Once nothing references it anywhere in production code, then delete it from the model. The trap is reversing those steps. If you delete the field while old code is still reading it, you risk null reference errors at runtime. Defensive code that null-checks every field read is the cheapest defence against this. Build that habit from day one and most "deletions" never need a separate environment to validate.

Type changes are the dangerous case. Two things have to work:

  1. Existing field values must be valid in the new type. Text to rich text is usually safe. Text to number is not. Single-select to multi-select is fine. Multi-select back to single-select loses data.
  2. Code must handle the new shape. Cached or unpublished items may still hold the old shape for a while. The reader has to be ready for both.

For type changes, the cleanest pattern is usually a new field plus a migration, not editing the existing field in place. Add the new field, populate it from the old one, update the code to read the new field, hide the old one, eventually delete it. Slower but reversible at every step.

This is where a sandbox earns its keep. Type changes are exactly the case where running the change in an isolated instance first is genuinely useful. You can confirm what happens to existing values before touching production data. For purely additive changes and well-managed deprecations, a sandbox is nice to have, not necessary.

Where the CLI fits. Model changes can be synced between instances using the CLI when your topology calls for it. The sync is the mechanic. The additive-then-deprecate-then-delete discipline above is the strategy. The CLI does not relieve you of the discipline. A destructive type change synced from DEV to PROD loses the same data as one made directly in the production UI.

Pick your topology

Most customers fit one of three topologies. Pick yours before you read the workflow sections.

The three options:

  1. Single instance. The default. Editors and developers both work in production. Code is tested locally and in staging website deployments, all pointing at the production CMS.
  2. Production plus sandbox. Developers add a sandbox instance for testing model and architecture changes. Editors stay in production. Architecture changes are promoted from sandbox to production using the Agility CLI.
  3. Multi-environment. Two or more instances representing genuine separate environments (typically DEV, UAT, PROD), justified by either compliance requirements or by a consuming product that needs realistic non-production content for QA.

The rest of this guide walks through each.

Coming from Contentful or Sanity? Their model nests environments inside a top-level workspace, and you spin up additional environments by cloning. Agility treats each instance as a fully isolated, production-grade environment. Tradeoff: you cannot clone an environment in three clicks, but every Agility instance can be configured with its own SLA, versioning depth, and workflow rules to match its specific role. Most customers have different requirements for sandbox versus production, and that is fine. Use the CLI's clone or sync operations for the equivalent of spinning up and refreshing a sandbox.

Single instance workflow

This is what most customers need. One production Agility instance. Editors and developers both work in it. The website code is deployed to multiple environments (local, dev, UAT, prod), all pointing at the same instance.

Developer workflow

  1. Model your content in the CMS. Be cautious about breaking changes to existing models.
  2. Initialize test content in Staging/Draft. Do not publish.
  3. Build the rendering code in your local website environment in preview mode.
  4. Test against the Staging/Draft content.
  5. Deploy your code to dev-test, QA, UAT, and production. Register each deployment in Web Studio so editors can preview content on any of them.

Editor workflow

  1. Editor creates content. It enters Staging/Draft and is visible on any registered deployment in preview mode.
  2. Editor requests approval. The item moves to Awaiting Approval and is still preview-only.
  3. Approver opens the item's preview URL on the registered deployment of their choice (typically UAT) and approves or declines (sends back to Staging/Draft).
  4. Once approved, the item moves to Approved. Still preview-only.
  5. The item is published. It now appears on the live URL of every registered deployment, including production.

This workflow handles the majority of customers. Resist adding complexity until you have a concrete reason that this workflow does not solve.

Production plus sandbox workflow

Add a sandbox instance when your team has multiple developers working on the content model simultaneously, or when you regularly need to test destructive model changes (field type changes especially) without risking production data. See "Model changes and the code that uses them" above for which changes warrant this and which do not.

When to introduce the sandbox

Build v1 in production. Add the sandbox after.

This sounds backwards if you are coming from a code-first mental model, but it is the right sequencing for content. The sandbox exists to protect a working production instance from destructive experiments. Until you have a working production instance, there is nothing to protect, and the sandbox has nothing realistic to test against. Cloning an empty instance into another empty instance is theatre.

Once production is live and your editors are working in it, clone production to create the sandbox. From that point forward, model changes that warrant isolation start in sandbox and promote upward, and the sandbox gets refreshed from production whenever its content drifts too far from reality.

The principle that makes this work: architecture promotes upward, content does not.

What flows where

  • Architecture (content models, containers, page templates, modules) flows from sandbox to production. Promote frequently. Treat it as code.
  • Content (actual entries, pages, assets, galleries) generally flows from production to sandbox as a refresh, so developers test against realistic data. Promote rarely.
  • Reference content (dropdown lookups, navigation labels, form copy) travels with architecture. Treat as part of the release.

Three ways to move architecture between instances

The Agility CLI is a single npm package (@agility/cli) with the commands login, pull, push, clone, and sync. How you use it depends on where you are running it and whether the run needs to be repeatable. There is also an MCP server that exposes the same operations through an AI assistant. Three real paths:

  • Locally on a developer machine. Authenticate once with agility login, which opens a browser to complete the OAuth flow. Then use pull, push, and clone for ad-hoc operations: cloning prod to a fresh sandbox, pulling models down to inspect them, pushing a one-off architecture change. This is the right starting point for any team adopting the sandbox topology.
  • In CI/CD with agility sync. Same CLI, run with the --headless flag and authenticated via a Personal Access Token instead of a browser. The sync command persists a mappings file alongside your project that records the relationship between source and target IDs. Commit the mappings to your repo and sync becomes idempotent: running it twice does not create duplicates. This is the right path once you have a stable promotion process worth automating.
  • Through the MCP server. Connect to mcp.agilitycms.com from Claude or another MCP-aware client, point at your source and target instances, and describe what you want moved at the level of intent ("clone the Press Release model and its containers from sandbox to prod") instead of remembering CLI flags. The MCP server uses the same underlying APIs and produces the same result. Useful for complex one-off migrations where you would otherwise be flipping back and forth through CLI documentation. The MCP server authenticates per user via OAuth, so it is for interactive human-plus-AI use, not a replacement for agility sync in unattended CI pipelines.

All three paths work against the same instances and can be used on the same project. Most teams start with the local CLI for exploration, graduate to agility sync in CI once the promotion shape stabilises, and reach for the MCP server for the operations that fall in between.

For installation, command syntax, flags, mappings details, and PAT setup, see the CLI references at the bottom of this guide.

What the CLI does and does not move

The CLI can move models, galleries, assets, containers, content, templates, and pages. These can be moved together or scoped down to specific elements (for example, models only, or a specific model and its dependencies).

The CLI does not move: users, roles, permissions, webhooks, integration configurations, app installations, or API keys. These are instance-local by design. Configure them per instance.

Multi-environment workflow

This is the topology for genuine separation between non-production and production content surfaces. Most customers do not need this. Justified scenarios:

  • Regulated industries (financial services, healthcare, insurance) where change control requires demonstrable separation between authoring and the live publishing surface, beyond what a workflow inside a single instance provides.
  • Consuming products with their own DevOps environments where the product's UAT environment needs realistic content for QA validation, and using production content would either leak unreleased product features into prod or pollute prod with test data.
  • Multi-tenant solutions where customer environments are provisioned per-instance.

If your scenario is "we want to feel safer," you do not need this. The single-instance workflow with approvals is already audit-ready. If your scenario is "our developer team has DEV, UAT, and PROD for the website code," you do not need this either. Multiple website deployments pointed at one instance is the right answer.

Topology

  • DEV instance. Developer-owned. Editors do not work here. Refreshed on demand from UAT or PROD using the CLI. Used to validate model changes and CI scripts before they touch UAT.
  • UAT instance. Editors stage release content here when the consuming product release requires it. QA validates against the UAT build of the consuming product. Architecture flows in from DEV via CI. Content seeds from PROD as needed.
  • PROD instance. Live editorial work. Published content goes live. Architecture flows in from UAT via CI as part of the release.

Promotion direction

  • Architecture promotes upward: DEV to UAT to PROD, via the CLI running in CI/CD.
  • Content promotes downward: PROD to UAT to DEV, via the CLI on demand.

This is the opposite of the database mental model. People expect everything to flow upward like code. It does not. Real content lives in PROD. Lower environments get refreshed from PROD when fixtures are stale.

Mappings: the key to repeatable sync

When the CLI runs in CI/CD, it persists a mappings file alongside your project. The mappings record the relationship between content IDs in the source instance and content IDs in the target instance. Commit the mappings to your code repository.

Why this matters:

  • Without mappings, every sync run treats target content as new and creates duplicates.
  • With mappings, sync is idempotent. Running it twice does not double the content.
  • Mappings give you a Git-native audit trail of every sync operation.
  • They give you rollback capability. Revert the mappings commit and you revert the sync state.

If you treat mappings as throwaway, you will get duplicate content. Treat them as part of your codebase.

Authentication and roles

Browser-based login does not work in CI environments. Pipelines authenticate using a Personal Access Token (PAT) instead.

  • PATs are generated through the Management API. See the CLI - CI/CD Integration Guide for the procedure.
  • The user associated with the PAT must have one of: Org Admin, Instance Admin, or Manager role on both source and target instances.
  • Store the PAT in your CI platform's secret store, never in source control.
  • Run all CI commands in headless mode so they do not block on interactive prompts.

CI/CD pattern

A typical pipeline for architecture promotion looks like this conceptually:

  1. A change lands on a release branch in your code repository, or someone triggers the pipeline manually.
  2. The pipeline checks out the repository, including the existing mappings file.
  3. The pipeline runs the CLI sync from the source instance to the target instance, scoped to architecture elements only (models, containers, templates).
  4. The pipeline commits the updated mappings file back to the repository so the next run is idempotent. The commit message includes a marker that prevents the commit from triggering another pipeline run.

The same pattern repeats for each promotion stage. DEV to UAT runs on release branches. UAT to PROD runs on main or on a manual approval gate.

For full pipeline templates and platform-specific examples (GitHub Actions, GitLab, Azure DevOps), see the CLI - CI/CD Integration Guide.

Driving editorial state from CI

The CLI also lets your pipeline drive editorial workflow operations on the synced content. After a sync, the same pipeline can publish, approve, decline, request approval, or unpublish the items it just moved.

Three patterns worth naming:

  • Architecture promotion to PROD: sync, then publish, so model changes go live as part of the release.
  • Content refresh into UAT for QA: sync, then request approval, so QA validates items in Awaiting Approval on the preview API without anything going live.
  • DEV instance refresh from UAT: sync only. Leave content in Staging/Draft. Developers do not need an approval workflow on their dev instance.

This is the piece that makes the multi-environment topology coherent. Without it, you would sync content into UAT and then manually walk it through approval. With it, the CI pipeline owns the end-to-end promotion. Most prospects evaluating Agility for multi-environment use do not realize this exists.

Compliance and auditability

If your driver is regulatory, the pieces of the audit story are:

  • Workflow approvals in the production instance: who approved what, when.
  • Content version history in the production instance: what changed and who changed it.
  • CI/CD logs and Git commits for architecture promotion: which model changes were synced and by whom. The mappings file in Git gives you the diff.
  • PAT-scoped access: each automated pipeline has its own token, attributable in audit logs.
  • Instance separation: editors and developers have access only to the instances appropriate to their role.

Multi-instance separation is one piece of this. It is not the whole picture. Strong workflow inside a single instance often satisfies more of the audit story than people assume. Confirm with your auditor before assuming you need multiple instances.

Anti-patterns

Things to avoid:

  • Using sitemaps as environments. A sitemap is for one website or app. Editors should never be asked to maintain "draft," "staging," and "production" copies of the same page in different sitemaps. See "Sitemaps and channels are not environments either" earlier in this guide.
  • Bidirectional content sync. Pick a direction for content (down) and stick to it. Two-way sync creates conflicts and reference drift.
  • Discarding the mappings file. If your CI ignores or does not commit the mappings back to the repository, every run creates duplicate content. Always persist mappings.
  • Running sync from local machines and from CI for the same target. Local runs produce mappings on a laptop that CI does not see, and vice versa. Pick one source of mappings per target instance.
  • Scheduled clone or sync from production into a sandbox without confirmation. This will overwrite in-progress developer work. If you automate refresh, gate it behind manual approval.
  • Concurrent CI runs against the same target instance. Mappings will conflict in Git. Serialize promotions through environment locks or branch-protected sequential pipelines.
  • Rolling out destructive field-type changes via sync without a migration plan. See "Model changes and the code that uses them" above. The CLI does the move. It does not do the migration.
  • Provisioning multi-environment topology by default for "compliance". Confirm the actual requirement first. Strong workflow in a single instance covers most audit needs.
  • Asking editors to work across multiple instances routinely. Editors should have one home. If your topology forces editors to context-switch across instances daily, your topology is wrong.

CLI reference and limits

For the full command reference, see:

  • CLI - Push / Pull / Clone for ad-hoc local operations
  • CLI - CI/CD Integration Guide for headless automation, PAT auth, mappings, and workflow operations

Known limits worth setting expectations around:

  • The CLI is locale-scoped. Multi-locale instances either run per-locale or pass multiple locales together. See the CLI docs for syntax.
  • Cross-instance reference handling is limited. Content with references to items not present in the target instance may fail or partially apply.
  • Field-type changes do not migrate via sync. Plan an additive-then-deprecate migration for any breaking model change. See "Model changes and the code that uses them" earlier in this guide.
  • Sync logs are written to disk. Retain them as CI artifacts for debugging.

Website deployments

You can have as many website deployments as you want per CMS instance. A typical setup includes local development plus deployments for dev-test, QA, UAT, and production. The exact set varies by team. Some teams have only UAT and production. Larger teams may have additional environments for performance testing, regional rollout, or customer-specific staging.

Register every non-local deployment in Web Studio. Once registered, each deployment supports two modes:

  • Preview mode returns Staging/Draft, Awaiting Approval, Approved, and Published items. Editors use preview URLs to validate unpublished content.
  • Live mode returns Published items only. End users see live URLs by default.

Both modes are available on every registered deployment, including production. Editors can preview unpublished content on the actual production website, on UAT, or on any other registered deployment, by copying the appropriate URL from Web Studio. Local development is the only deployment that typically does not register with the CMS, because it is not a stable URL editors would point at.

Whether a single deployment actually serves both modes depends on the website framework and how it is implemented. Our recommended approach is Next.js using the example code we ship, which has preview and live mode switching built in. Sites built on other frameworks need this wired up explicitly. If dual-mode preview is important to your editorial workflow, confirm your chosen framework supports it before committing to a topology.

For multi-environment topologies, each CMS instance has its own corresponding set of registered deployments. The preview/live mode distinction applies to every registered deployment, in every environment. There is no environment that is "preview-only" or "live-only" by virtue of being non-production. The mode is chosen by the URL the user opens.