The Kentico SaaS Migration: Lessons Learned and Troubleshooting Tips

In Part 1, I walked through migrating my Xperience by Kentico site from self-hosted to SaaS. āœ…

In Part 2, I set up automated deployments using Azure DevOps to streamline the process. āœ…

In this final part, I’m sharing the lessons I learned, the gotchas I encountered, and the troubleshooting tips that helped me work through them. These insights come not just from this migration, but also from working on SaaS projects with the wider team at IDHL. Hopefully, they'll help save you some time or at least a bit of frustration.

🧩 Content Authoring During Development

One of the first challenges was figuring out how content editors and testers can safely make changes in environments where developers are actively building and deploying features.

When using Continuous Deployment (CD), having the correct configuration is key. It's easy to forget to adjust it, push a deployment to QA, and boom šŸ’„ the testers work gets overwritten by local dev content.

My tip: clear communication is vital. Everyone should know when a deployment is happening, and whether it might overwrite content. If you're new to CD configuration files, have a teammate review your setup and suggest improvements.

One more thing, real content should be added directly in the production environment, at least until Content Sync is released in one of the upcoming refreshes.

šŸ†• Applying Refreshes

If you're using a seeding database, either with Continuous Integration (CI) or a pipeline like the one I showed in Part 2, make sure it's updated after applying each Refresh.

If not, your automated pipeline, or any new developer setting up locally, may run into a version mismatch error like this:

fail: Kentico.Web.Mvc.IStartupConfigureAction[0]
      Startup action 'Kentico.Web.Mvc.CIRestoreService'.
      System.NotSupportedException: Xperience Continuous Integration or Deployment can run only on same version as '30.3.0'.
         at Kentico.Web.Mvc.CICDServiceDatabaseVersionChecker.ThrowException(Version assemblyVersion)
         at Kentico.Web.Mvc.CICDServiceDatabaseVersionChecker.IsMatch(Version dbVersion)
         at CMS.DataEngine.CMSApplication.Init()
         at Kentico.Web.Mvc.StartupConfigureActionBase.InitCmsApplication()
         at Kentico.Web.Mvc.StartupConfigureActionBase.Initialize()
         at Kentico.Web.Mvc.ExitCodeService.Initialize()

My tip: make sure all developers understand how important it is to keep the seeding database up to date. If you're reviewing a pull request that includes a hotfix or refresh, double check whether the updated database file is included too.

1ļøāƒ£ The Initial Deployment

If you're migrating a website from self-hosted to SaaS, your first deployment package is likely to be large, especially if you're including all the site code, media, and assets. This can easily push you over the 2GB limit for SaaS deployment packages.

Kentico does plan to improve this in the future. But for now, if you do exceed the limit, you'll need to contact Kentico Support to manually import the package.

My tip: after your initial deployment, take time to review your CD configuration file. Only include what's truly needed in future packages. You can also break down deployments into smaller chunks, for example start with just the essentials object types and then include more with follow up deployments.

šŸ’¾ Update Your .gitignore File

If you're new to CI/CD as part of moving to SaaS, you'll need to be extra careful about what gets committed to source control.

As covered in Part 2, the CD configuration file should be committed, but the CD output files should not.

Make sure you exclude the output directory from source control.

āš ļø Check For Legacy Files

I had a leftover web.config file from my self-hosted setup that was setting the ASPNETCORE_ENVIRONMENT value.

On my first SaaS deployment, that setting overwrote the SaaS environment config, which broke the database connection string in QA and caused a lot of confusion! šŸ˜•

My tip: audit your config files and make sure you've removed anything you no longer need.

While you're there, updated your environment specific appsettings files to match your new SaaS environments, appsettings.Production.json, appsettings.Qa.json, or appsettings.Uat.json.

#ļøāƒ£ Re-sign Your Macros

Remember, in Part 1 we updated the CMSHashStringSalt to match the one provided in the Xperience Portal.

You'll also need to re-sign your macros, otherwise things in the admin UI will start to break in subtle ways.

I forgot to do this on my first deployment, and I only realised when the Edit Page button suddenly stopped working.

My tip: re-sign them straight away using the following command:

dotnet run --no-build -- --kxp-resign-macros --old-salt "Old_Salt" --new-salt "New_Salt"

šŸ’„ Custom Modules and Community Packages

My blog uses an open-source package I built for the community: Content Security Policy (CSP) Management. It works well, but it tries to install a scheduled task as soon as the site boots up.

On my first SaaS deployment, the database hadn't finished restoring before the CSP module tried to install the task. This would have been hard to diagnose without access to the event logs in the Xperience Portal.

I downloaded the deployment logs and immediately stopped the problem, the required database tables didn't exist yet! 🤭

My tip: check the event logs for deployment failures, they are incredibly useful.

Also, if you run into issues with community packages, raise an issue in the GitHub repository. Someone in the community (or the author) will likely get around to addressing it.

In my case, I temporarily uninstalled the NuGet package for the first deployment, and then reinstalled it in the next one. šŸ‘

ā˜ļø Cloudflare WAF

I'm glad Kentico chose to use Cloudflare for SaaS, it's also our default recommendation for clients at IDHL.

It works really well, but depending on your configuration, you might accidentally block legitimate requests with some of the WAF rules.

When I first deployed to SaaS, everything looked great, until I opened one blog post in particular in Page Builder mode.

Cloudflare WAF was blocking one of the widgets because it detected suspicious markup. It was a bit inconvenient, but it is also reassuring that the WAF was clearly doing its job.

Luckily, you can grab the Cloudflare Ray ID from your browser's network tab and send to Kentico Support. They'll review the logs, and make any necessary adjustments to rules.

Wrapping It Up

Kentico SaaS takes a lot of the infrastructure and maintenance burden off your plate, but it does introduce a few new habits and adjustments along the way.

Once you've figured it out, it becomes a smooth, repeatable, low stress progression from a self-hosted setup.

And that wraps up my Kentico SaaS Migration series - for now? šŸ¤”

My SaaS trial is coming to an end, but I'll continue blogging about Kentico SaaS with learnings from future project work.

You might also be interested in...