Distinguish Between Definition and Content Items
In XM Cloud, serialization is primarily a developer/ops concern, not a task for content authors. Developers and DevOps engineers use the Sitecore CLI to manage definition items (templates, layouts, SXA settings, etc.) in version control, whereas content authors continue to work in the Content Editor/Experience Editor and publish content via the usual publishing pipeline. In contrast, content authors simply publish content; they shouldn’t be expected to run CLI commands: we reserve the CLI workflow for dev-defined items, and let content authors handle content via Sitecore’s authoring tools.
In XM Cloud, all templates and configurations are the definition items in the Sitecore tree - many of these are delivered as items-as-resources in protobuff resource files. By default, XM Cloud provides a base set of definition items in resource packages; custom definitions (such as your templates, renderings, layouts, SXA component definitions) should be added via serialization. The CLI can push/pull any item in the tree, so treat your custom definitions like any other serialized item. For example, you would include custom layouts (under /sitecore/Layout/Layouts/YourTenant
), rendering definitions (/sitecore/Layout/Renderings/YourTenant
), SXA rendering variants, themes, and placeholder settings in your modules. In fact, the Sitecore docs note that SCS modules allow you to "organize and separate serialized items according to their purpose".
You generally do not manually transfer definitions by hand; instead, you define them in your project and let the CLI handle serialization. As a special tool, Sitecore provides an Items as Resources CLI plugin (dotnet sitecore itemres
) to package definition items into *.DAT resource files, but in most cases, you simply let sitecore ser push
pack the YAML and deploy it to XM Cloud.
Note, that some environment-level settings are not items. For example, custom Sitecore security domains live in a config file on the server, not in the content tree, so they cannot be serialized or deployed via CLI. In those cases, you must handle them outside of SCS.
In summary: treat definition items as code: include them in your serialization modules and commit them, and let the Sitecore CLI bundle them into the XM Cloud resource IAR package. Content items, by contrast, should be managed by content workflows.
Content Serialization
Generally, don’t serialize day-to-day content. XM Cloud follows the old wisdom of treating content differently from code: the CLI is not intended as a general content migration tool. Instead, content authors create or edit content items in the CM interface or Pages Builder and then publish them to delivery targets. Serializing content will lead to conflicts and source-control bloat.
That said, there are a few exceptions where you might include minimal content stubs or settings in serialization. For example, it’s common to serialize the homepage item itself so that a brand-new environment has the correct site entry point immediately upon first serialization. You'd want to explicitly include the tenant and site root with CreateAndUpdate
operations - this ensures that new environments get a placeholder homepage item (and updates propagate), without overwriting content. You might also serialize certain settings or dictionary items that are truly "configuration" (say, a language items folder or a set of shared data templates). But actual content (articles, product entries, news items) should be kept out of serialization.
In practice, restrict content serialization to very small, static subsets (often at most the single site-root node) and mark them as create-only or create-and-update. For example:
{
"name" : "TenantA_SiteRoot",
"path" : "/sitecore/content/TenantA/SiteA,"
"scope": "singleItem",
"allowedPushOperations" : "CreateUpdateAndDelete"
}
This would ensure the /SiteA
item exists in all environments, but leave its children alone. The rest of /sitecore/content/TenantA/SiteA
descendants (actual page content) would not be included.
Remember that after you push any content items, you still must publish them to make them live. The ser push
command only writes into the CM database; it does not publish to Experience Edge. To serve content, you must run a publish command (dotnet sitecore publish --target Edge
).
Summarizing: Limit content serialization to definition items with some exceptions like an empty homepage or settings, and let normal publishing handle real content.
Also, "do not include vanilla XM Cloud items – include only custom-created items". Content changes by authors should flow through Sitecore’s normal publishing workflows, not through SCS commits.
Serialization Scopes
Sitecore CLI modules use scopes to control how deeply an include or rule applies. The scope
property can be one of:
-
SingleItem
– only the specified item itself (no children).
-
ItemAndChildren
– the item plus its immediate (one-level) children.
-
ItemAndDescendants
– the item and all levels of descendants - the full subtree. That is a default value.
-
DescendantsOnly
– all descendants of the item, but not the item itself.
-
Ignored
– skips this branch entirely, used in rules to exclude subtrees.
For example, suppose you include a path /sitecore/content/TenantA/SiteA
in a module. If not specified, that is ItemAndDescendants
by default, so it would pull the entire site tree. If you want only the root item and none of its children, you would add a rule with "scope": "SingleItem"
. Conversely, to skip an unwanted subfolder, you could use "scope": "Ignored"
on that path.
A concrete example of a rules section might look like:
"items": {
"includes": [
{
"name": "SiteA",
"path": "/sitecore/content/TenantA/SiteA",
"allowedPushOperations": "CreateUpdateAndDelete",
"rules": [
{ "path": "/sitecore/content/TenantA/SiteA/Data", "scope": "Ignored" },
{ "path": "/sitecore/content/TenantA/SiteA/Home", "scope": "Ignored" },
]
}
]
This says "include the SiteA item (with all descendants by default) but ignore the entire Data and home Page subfolders." In practice, you’ll define scopes to precisely include what you need: broad (ItemAndDescendants
) for full branches, and narrow (SingleItem
or Ignored
) to exclude or limit depth.
Serialization Order and Modules
Since you organize your serialized items into modules within each .module.json
file in the Sitecore CLI, you must somehow define which modules depend on stuff from others. This each module can have an optional references
list to other modules. The CLI uses these references to enforce a load order. For example, if you have a Foundation module with templates (Foundation.CoreTemplates
) and a Feature module that depends on them (Feature.Shop
), you would write:
{
"namespace": "Feature.Shop",
"references": [ "Foundation.CoreTemplates" ],
"items": {
"includes": [
{ "name": "ProductTemplates", "path": "/sitecore/templates/Feature/Shop", "allowedPushOperations": "CreateUpdateAndDelete" }
// ...
]
}
}
By specifying "references": [ "Foundation.CoreTemplates" ]
, you ensure the CLI pushes/pulls Foundation templates first, then the shop items. Wildcards are allowed ( "Foundation.*")
to reference all Foundation modules. If you omit references, the CLI may process modules in alphabetical or file order, which can lead to missing-dependency errors, such as pushing a rendering before its template exists.
In practice, follow Helix conventions: have a base module for core templates/branch templates, then project-level modules that reference it and use the references
array in each module to declare dependencies.
Alternatively, you can also tag modules in CLI commands (with -i
) to run only subsets, but the fundamental sequencing comes from references
. If dependencies are missing or misordered, you will see errors during push (like “item not found” or unresolved references). Always review dotnet sitecore ser info
to verify your module graph.
SXA Serialization Considerations
When using SXA in XM Cloud, treat SXA definition items as code. As usual, you should serialize your SXA component definitions and site structure, but not your actual content. Key SXA assets to include are:
-
Rendering Variants and Templates: Anything under /sitecore/layout/Rendering Variants/...
and the SXA Page/Partial designs under /sitecore/layout/Layouts/...
or /sitecore/content/TenantName/SiteA/Presentation/...
.
-
Placeholder Settings and Layouts: SXA uses placeholders and page layouts, which live under /sitecore/Layout
or under the site; include those as part of layouts and placeholders sections.
-
Themes and Styles: If your SXA site uses a custom theme (under /sitecore/media library/Themes
or the SXA Theme item), serialize that.
-
SXA Site Settings: Under each site (e.g. /sitecore/content/Tenant/SiteA/Settings
), include any site-level settings items that define colors, etc., if they are required for deployment.
-
Site Templates: SXA allows site templates in the dashboard; if you have custom site templates, serialize those under /sitecore/templates/Project/...
.
Do not serialize authored page content (articles, products, etc.) from SXA sites. Also consider shared vs. site-specific: if multiple sites under one tenant share variants or partial designs, put them in a shared module (under the tenant node). If a design is only for SiteA, put it under the SiteA module. In general, follow the same include rules as above, putting SXA assets under your project/feature paths. For example, include renderings and placeholder settings under /sitecore/Layout/Renderings/Project/...
and /sitecore/Layout/Placeholder Settings/Project/...
- these would cover your custom SXA renderings and placeholder definitions as well. By version-controlling SXA assets in the CLI, you ensure that your site designs, variants and styles are consistent across environments, while leaving content (pages and datasources) to the content pipeline.
Module Organization
For the multisite and especially multi-tenant data architecture, it becomes crucial to organize your modules and serialization in a totally isolated way. Thus, everything becomes self-contained so that removing one site/ tenant folder does not affect the rest of the serialized assets. To achieve that, I would recommend doing this:
- Organize all the assets under a specific folder, for example: authoring/items/client
- All the relevant modules fall into this folder. I usually split them into three groups: client.global.module.json; client.components.module.json and client.site.module.json
- Each of these module files must include a path directive, which effectively defines a subfolders structure: "path": "components" or "path": "site". This is the most crucial bit.
- Nothing else exists immediately under authoring\items folder other than clients and areas; typically there would be a global folder followed by client1, client2, etc.
I would also recommend reading this article, which I found to be helpful. Hope you find these tips helpful!