HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Upgrade to CMS 13 from CMS 12

Describes the main process for upgrading to Optimizely Content Management System (CMS 13).

This article assists developers and implementers in upgrading their Optimizely CMS solutions from versions 12 to the latest CMS 13. It outlines the updated migration strategy, including the deprecation of older tooling and the recommendation of AI-powered assistance to streamline the modernization process.

For details about CMS 13 platform changes and upgrade considerations, see CMS 13 breaking changes.

🚧

Important

CMS 13 runs on .NET 10 and newer. When upgrading from earlier CMS versions, ensure that your solution targets the required .NET version before beginning the migration.

Previous migration tools for Optimizely CMS have been deprecated. Microsoft recommends using GitHub Copilot or similar AI-assisted development tools to help modernize and update your codebase during the upgrade process.

Review mandatory components for CMS 13

Before starting, review Overview of CMS 13 to understand the mandatory and optional components of CMS 13. You should consider the timelines needed for each component in your go-live planning.

Leverage AI with GitHub Copilot for modernization

GitHub Copilot’s app modernization chat agent offers significant advantages during Optimizely CMS upgrades:

  • Accelerated development – Automates repetitive coding tasks and suggests solutions for common migration challenges.
  • Reduced error rates – Helps identify potential issues and provides correct syntax/API usage.
  • Knowledge transfer – Acts as an intelligent assistant, providing context-aware suggestions for new frameworks and libraries.
  • Efficiency – Speeds up the overall migration timeline, allowing teams to focus on higher-value tasks.

For detailed instructions on using GitHub Copilot, see the official Microsoft and GitHub documentation. Always ensure thorough testing after any migration to guarantee the stability and functionality of your Optimizely solution.

Upgrade to CMS 13

Upgrading from CMS 12 to CMS 13 builds upon the existing .NET foundation but introduces significant changes to service registration, API surfaces, and the site management model. This section walks through the upgrade process step by step.

Prerequisites

  • Existing CMS 12 solution – Your application should already be running on Optimizely CMS 12.
  • .NET 10 SDK – CMS 13 requires .NET 10. Packages will fail dotnet restore on earlier .NET versions.
  • Database backup – Create a full backup of your CMS database before starting.
  • IDE – Visual Studio 2026 (18.0+) or a compatible IDE with .NET 10 support.

Pre-upgrade: Audit and prepare

Before changing any packages or code, audit your solution for potential compatibility issues.

❗️

Warning

Search & Navigation (EpiServer.Find) is not supported in CMS 13. Refer to Migrate from Search & Navigation to Optimizely Graph to replace the legacy content indexing, preview, and delivery pipeline. You can choose to implement search with Optimizely Graph or bring your own search provider or choice.

  • Inventory third-party packages – Check each non-Optimizely NuGet package for a CMS 13-compatible version. Remove or disable incompatible packages before upgrading Optimizely packages. See Optimizely and third-party packages breaking changes for a list of commonly used packages and their compatibility.
  • Document customizations – Note any custom scheduled jobs, custom content types, initialization modules, and integration points that may need updates.
  • Resolve all obsolete warnings – APIs marked as [Obsolete] in CMS 12 have been removed in CMS 13. Before upgrading, resolve all obsolete warnings in your CMS 12 build. Consider enabling <TreatWarningsAsErrors>true</TreatWarningsAsErrors> in your .csproj file to catch these early. Any unresolved obsolete usage will become a compilation error after upgrading.
  • Review Optimizely Graph queries – The Optimizely Graph schema in CMS 13 contains breaking changes compared to CMS 12. Frontend applications that query Graph will require updates to reflect the new schema. Review your Graph queries and update them before going live.
⚠️

Important

Migrating DAM from CMS 12
If you are using DAM with CMS 12, you should not attempt to migrate your assets until migration tools are available to migrate your DAM assets to CMS 13. Other aspects of the CMS 12 to 13 upgrade can proceed in the meantime. The migration tools are prioritized and will be released in a version after CMS 13.0.0.

Third-party package compatibility
Third-party package incompatibility is typically the single largest area of effort during a CMS 13 upgrade. Packages such as Geta.NotFoundHandler.Optimizely, Geta.Optimizely.Sitemaps, Geta.Optimizely.GenericLinks, Geta.Optimizely.ContentTypeIcons, and Advanced.CMS.AdvancedReviews may not yet have CMS 13-compatible versions. Removing these packages can cause cascading effects, including lost transitive dependencies and orphaned content types in the database. Plan for this work before you begin.

Removed Features
Dynamic Properties, Mirroring, and the legacy Plugin system were deprecated in CMS 12 and are fully removed in CMS 13. All related classes, interfaces, stored procedures, and database tables have been removed, including export/import APIs tied to these features. Search your codebase for references to the EPiServer.Core.DynamicProperty*, EPiServer.PlugIn, and mirroring-related transfer types, and remove or replace them before upgrading. See Breaking changes in CMS 13 for details.

Update target framework

Update your .csproj file to target .NET 10:

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

If you use Docker, update your base images to .NET 10 as well.

🚧

Important

After changing the target framework to net10.0, your solution will not compile until you update the NuGet packages in the next step. This is expected. Do not attempt to build at this point.

Update NuGet packages

Update all EPiServer.* and Optimizely.* packages to their CMS 13 versions:

<PackageReference Include="EPiServer.CMS.UI.AspNetIdentity" Version="13.0.0" />
<PackageReference Include="EPiServer.Cms.UI.VisitorGroups" Version="13.0.0" />

After updating, run dotnet restore to verify all packages resolve correctly.

dotnet restore

A successful restore completes with no errors:

Restore completed in 2.3s for MyProject.csproj.

Common issues at this stage

ErrorCauseResolution
NU1202: Package X is not compatible with net10.0Third-party package does not support .NET 10Remove the package or find a CMS 13-compatible version. See Optimizely and third-party package breaking changes.
NU1608: Detected package version outside of dependency constraintVersion conflict between Optimizely packagesEnsure all EPiServer.* and Optimizely.* packages target the same CMS 13 version (for example, 13.0.0).
Missing type errors after removing packagesLost transitive dependenciesExplicitly add the missing package (for example, Newtonsoft.Json).

If dotnet restore succeeds but dotnet build produces errors, proceed to the Fix API breaking changes section. Compilation errors from API changes are expected at this stage and are addressed there.

📘

Note

Removing packages like EPiServer.Find can cause you to lose transitive dependencies your code relies on, such as Newtonsoft.Json. If you encounter missing type errors after package cleanup, explicitly add the required package or migrate to System.Text.Json.

Update service registration

The service registration methods require the EPiServer.DependencyInjection namespace (not Microsoft.Extensions.DependencyInjection).

If your CMS 12 solution already uses AddCms(), this call continues to work in CMS 13 and is the recommended approach:

services.AddCms();

Internally, AddCMS() registers the following services:

services
    .AddCmsHost()
    .AddCmsCore()
    .AddCmsHtmlHelpers()
    .AddCmsTagHelpers()
    .AddCmsUI()
    .AddAdmin()
    .AddTinyMce()
    .AddCmsImageSharpImageLibrary()
    .AddOptimizelyCmsServicev1()
    .AddOAuthEndpoints()

Granular service registration

If your CMS 12 solution uses granular service registrations instead of AddCms(), review the list above and add any registrations your solution is missing.

The following packages have been removed as core dependencies from EPiServer.CMS in CMS 13. If your solution uses them, ensure they are explicitly referenced in your .csproj file and their services are registered:

  • EPiServer.CMS.UI.AspNetIdentity – Register with AddCmsAspNetIdentity<ApplicationUser>().
  • EPiServer.Cms.UI.VisitorGroups – Register with both AddVisitorGroupsMvc() and AddVisitorGroupsUI().

The following example shows a complete CMS 13 service registration including optional packages:

services
    .AddCmsAspNetIdentity<ApplicationUser>()
    .AddCms()
    .AddVisitorGroupsMvc()
    .AddVisitorGroupsUI();
⚠️

Important

CMS 13 uses assembly scanning to discover referenced packages at startup. Every NuGet package your project references must either have its services explicitly registered (for example, AddTinyMce() for the TinyMCE editor) or be completely removed from your project. If a package is referenced but not registered, the application will fail at startup.

CMS 13 requires the SQL Server database compatibility level to be 140 or higher. If you have an older on-premises database, enable automatic compatibility level updates in your Startup.cs:

services.Configure<DataAccessOptions>(options =>
{
    options.UpdateDatabaseCompatibilityLevel = true;
});

Verify service registration

After updating service registrations, run dotnet build to verify the configuration compiles. The build may still produce errors from API breaking changes (addressed in the following sections), but service registration issues typically produce distinct errors:

  • 'IServiceCollection' does not contain a definition for 'AddCmsCore' – Add using EPiServer.DependencyInjection; (not Microsoft.Extensions.DependencyInjection).
  • Startup crash: No service for type 'X' has been registered – A referenced package is missing its Add*() registration. Identify the package from the type name and add the corresponding registration, or remove the package entirely.
  • Startup crash: Unable to find a module by assembly 'X' – A removed package still has references in your project. Search for and remove all using statements and service registrations that reference it.

Enable Content Manager and Optimizely Graph

🚧

Important

If you are already using Optimizely Graph with CMS 12, before enabling Content Manager, complete the following:

  1. Add Graph to your project.
  2. Review the Graph schema comparison between CMS 12 and CMS 13.
  3. Migrate your Graph project to CMS 13 .
  4. Update your code to the CMS 13 schema.

Content Manager and Optimizely Graph are not enabled by default in CMS 13 and require explicit opt-in. Graph is mandatory to use the full capabilities of CMS 13 (see Overview of CMS 13). The following CMS 13 features depend on Graph infrastructure:

  • Optimizely Opal – Graph enables Optimizely Opal to index, interpret, and act on your content.
  • Content Manager – The editorial experience relies on Graph for content discoverability.
  • External content – Requires Graph indexing capabilities to connect external sources like DAM systems, PIMs, or other third-party applications, set up mappings, and manage content types to enable seamless access to external content.
  • Content binding – Uses Graph structured queries to connect content items to external data sources or other content items. It enables automatic synchronization and structured relationships between content items.

To add the NuGet packages, add the following to your .csproj file:

<PackageReference Include="Optimizely.Graph.Cms" Version="13.0.0" />
<PackageReference Include="EPiServer.Cms.UI.ContentManager" Version="13.0.0" />

Or run the following commands:

dotnet add package Optimizely.Graph.Cms
dotnet add package EPiServer.Cms.UI.ContentManager

To register the services, add the following to your Startup.cs :

services.AddContentGraph();
services.AddContentManager();
❗️

Warning

Register AddContentGraph() before AddContentManager() because Content Manager depends on Graph infrastructure. Reversing the order causes startup failures.

You should adopt Graph at the beginning of your project. Adding Graph to an existing CMS 13 solution requires more effort than including it during initial setup.

See Get Started with Optimizely Graph for CMS 13 to configure Graph.

Fix API breaking changes

After updating packages, run dotnet build to surface compilation errors from API changes. You can address these errors in any order as they are independent of each other. Continue fixing errors and rebuilding until dotnet build completes with zero errors.

The most common changes are listed below. For a complete list, see the CMS 13 breaking changes documentation and the API replacement map for a quick-reference lookup table.

SiteDefinition replaced by Application model

ISiteDefinitionResolver is replaced by IApplicationResolver in the EPiServer.Applications namespace. For code that used SiteDefinition.Current.RootPage, use ContentReference.RootPage instead.

// Before (CMS 12)
using EPiServer.Web;

public class MyController : Controller
{
    private readonly ISiteDefinitionResolver _siteResolver;

    public MyController(ISiteDefinitionResolver siteResolver)
    {
        _siteResolver = siteResolver;
    }
}

// After (CMS 13)
using EPiServer.Applications;

public class MyController : Controller
{
    private readonly IApplicationResolver _applicationResolver;

    public MyController(IApplicationResolver applicationResolver)
    {
        _applicationResolver = applicationResolver;
    }
}

Start page resolution simplified

Replace IApplicationResolver.GetByContext() calls that retrieve the start page with ContentReference.StartPage:

// Before (CMS 12 / early CMS 13 previews)
var website = _applicationResolver.GetByContext() as Website;
var startPageContentLink = website?.RoutingEntryPoint;

// After (CMS 13)
var startPageContentLink = ContentReference.StartPage;

The IRoutableApplication.EntryPoint property remains available for advanced scenarios that require explicit application context.

PageReference replaced by ContentReference

Replace all uses of PageReference with ContentReference and .PageLink with .ContentLink:

// Before (CMS 12)
PageReference pageRef = currentPage.PageLink;

// After (CMS 13)
ContentReference contentRef = currentPage.ContentLink;

ContentArea.FilteredItems removed

ContentArea.FilteredItems is removed. Use ContentArea.Items instead:

// Before (CMS 12)
var items = myContentArea.FilteredItems;

// After (CMS 13)
var items = myContentArea.Items;
📘

Note

ContentArea.Items may have different filtering behavior than the previous FilteredItems property. Test content area rendering thoroughly after this change.

IContentTypeRepository no longer generic

Remove the generic type argument:

// Before (CMS 12)
IContentTypeRepository<ContentType> repo;

// After (CMS 13)
IContentTypeRepository repo;

Service locator pattern replaced by dependency injection

Replace ServiceLocator and Locate.Advanced patterns with constructor injection:

// IInitializableModule before (CMS 12)
var loader = context.Locate.Advanced.GetInstance<IContentLoader>();

// IInitializableModule after (CMS 13)
var loader = context.Services.GetRequiredService<IContentLoader>();


// Service before (CMS 12)
public class MyClass
{
    public MyClass()
    {
        var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
    }
}

// Service after (CMS 13)
public class MyClass
{
    private readonly IContentLoader _contentLoader;

    public MyClass(IContentLoader contentLoader)
    {
        _contentLoader = contentLoader;
    }
}

Additional breaking changes

You should fix these to avoid compilation errors.

CMS 12CMS 13Notes
InitializationEngine.AssembliesAppDomain.CurrentDomain.GetAssemblies()Assembly discovery changed
Platform navigation HtmlHelpersTagHelpers (for example, <epi-navigation-primary>)UI helper migration

Deprecation warnings

These are non-blocking issues you should track for future cleanup. The following APIs are deprecated in CMS 13 and produce [Obsolete] compiler warnings. They continue to work in CMS 13 but are planned for removal in a future major version.

CMS 12/13 APIStatusAction
PropertyDefinitionTypePlugInAttributeDeprecated (planned removal in CMS 14)Suppress warning for now; plan replacement before CMS 14
IPageRouteHelperDeprecated (planned removal in CMS 14)Suppress warning for now; plan replacement before CMS 14

Check for build errors

Run dotnet build. When the build completes with zero errors, all blocking API breaking changes have been resolved. Remaining [Obsolete] warnings are non-blocking and can be addressed after the upgrade is stable.

📘

Note

If you still have errors, check the API replacement map for the specific type or method name. For errors not covered in this guide or the replacement map, consult the detailed breaking changes by topic articles.

Update admin URL references

The CMS admin path has changed from /EPiServer. The new path depends on your authentication setup:

  • DXP-hosted with Opti ID – The default path is /ui/CMS.
  • Self-hosted (without Opti ID) – The default path is /Optimizely/CMS.

Update any bookmarks, internal documentation, health checks, or hardcoded URL references in your codebase.

Configure the application model

CMS 13 replaces the sites concept with applications. The database migration automatically converts your existing site configuration into an In process application. After building and running your upgraded site for the first time, go to Settings > Applications in the CMS Admin interface and verify the migrated Application settings, including the start page and hostname.

Two application types are available:

  • In process – The default application type assigned to migrated sites to support backwards compatibility.
  • Headless – Provides stronger support for headless CMS scenarios with these key characteristics:
    • Renders actual content in a separate application (such as, Next.js, React, or Vue).
    • Delivers CMS content using Optimizely Graph.
    • Supports preview tokens for secure preview functionality.

See Application framework for more details.

Switch to the Headless application type

To switch your migrated application type from In process to Headless, complete the following steps:

  1. Navigate to Settings > Applications in the CMS admin interface.
  2. Delete the default In process application that the database migration creates.
  3. Create a Headless application.
  4. Set your start page as the application entry point.
  5. In the Hosts section, add your hostname as the default host.

Database and data considerations

The CMS 13 database migration runs automatically on first startup, but be aware of the following potential issues:

  • Schema migration failures – The schema migration may fail with SQL Error 1913 due to conflicting indexes. If this happens, manually drop the conflicting indexes identified in the error message, then restart the application to let the migration continue.
  • Tab name validation – CMS 13 enforces alphanumeric-only identifiers for tab names (no spaces or special characters). If your content types use tab names like SEO Settings, change the identifier to an alphanumeric value and use the [Display(Name = "SEO Settings")] attribute for the display label.

Test and stabilize

After completing the code migration, carry out the following steps:

  1. Resolve all compilation errors and review compiler warnings, which indicate further changes may be needed.
  2. Start the application and check the console output for startup exceptions or runtime errors.
  3. Confirm the CMS admin interface loads at /ui/CMS (or /Optimizely/CMS if self-hosting without Opti ID).
  4. Create, edit, preview, and publish content to verify the editorial experience works end to end.
  5. Verify pages render correctly, including content areas, blocks, and media.
  6. Check for [Obsolete] deprecation warnings in the build output. These are non-blocking but should be tracked for future cleanup before CMS 14.

Troubleshooting

  • "No policy found" errors in the admin UI – A removed package was registering authorization policies that menu items depend on. Ensure the package is fully removed and no references to it remain.
  • "Unable to find a module by assembly" errors – A removed package registered menu providers that are still referenced. Ensure the package is fully removed and no references to it remain.