Xperience by Kentico supports file system providers that allow you to map parts of the file system to Microsoft Azure Blob Storage (opens in a new tab). You can use Blob storage when:
Blob storage is particularly suitable for storing content item assets, and all other unmanaged binary files referenced by the Xperience application. Some application deployment environments, like Azure Web Apps (opens in a new tab) for example, do not guarantee a persistent file system for files created outside of the original deployment package. Therefore, if Azure needs to recycle the application due to rolling infrastructure updates or unexpected outages, all but the image with the original deployment is lost. Blob storage does not suffer from these limitations, as the infrastructure ensures redundancy in case of an outage.
Follow the instructions on this page to create Azure Blob storage providers for:
File name case
Unlike regular file systems (NTFS, VFAT), Azure Blob storage is case-sensitive. To ensure consistent behavior, Xperience automatically converts all file and folder names to lowercase when processing files on Azure storage.
Media libraries will be removed
Support for media libraries ended on 24 July, 2026. As of version 31.8.0, the feature is hidden in the admin UI for projects that have no media libraries, and it will be removed together with all associated APIs in the near future.
Before mapping media library files to the Microsoft Azure Blob Storage (opens in a new tab), consider migrating media libraries and mapping content item assets instead. See Media library migration for instructions on how to migrate your media library files to Content hub.
Media library files stored in Azure Blob storage have the following limitations:
When developing an Xperience application that you want to deploy to the SaaS environment, use the storage path mapping system to route application files to Azure Blob Storage.
Azure Blob storage is part of your Xperience by Kentico SaaS subscription and is used when deploying projects to the SaaS environment, as storing persistent data alongside Xperience application binaries deployed in Azure App Service (opens in a new tab) is not recommended.
Managed and unmanaged data
Only data that is not handled by the CI/CD features and is stored outside of the Xperience database needs to be deployed to the Azure Blob Storage.
Projects created with the --cloud parameter include AddXperienceCloudStoragePathMapping() in Program.cs and the Export-DeploymentPackage.ps1 deployment script. Xperience provides Azure Blob storage with the required accounts as part of every subscription.
The default configuration automatically maps all system paths based on the detected environment:
SharedPersistent paths (content item assets, media libraries, AIRA images, files uploaded via the Upload file form component) are mapped to Azure Blob Storage. The default container name is default.All system paths are registered automatically by Xperience modules. See System-registered paths for the full list.
To use a different container name or multiple containers for different paths, configure the options callback in Program.cs. The following table lists all available options:
| Property | Type | Default | Description |
|---|---|---|---|
ContainerName | string | "default" | Azure Blob container name in cloud environments. In local development, ContainerName becomes a subdirectory under StorageAssetsFolderName, making the local layout mirror the cloud storage structure. The deployment pipeline collects files from StorageAssetsFolderName/ContainerName/ and transfers them to the Azure Blob container of the same name. |
StorageAssetsFolderName | string | "$StorageAssets" | Root directory for deployment-packaged paths on local disk. Only affects the local development layout. If you change this value, update the Export-DeploymentPackage.ps1 script to collect files from the new root folder. The default script collects from $StorageAssets/. |
ConfigureContainerForPath | Action<PathRegistration, ContainerSetup> | null | Callback that overrides ContainerSetup.ContainerName for specific paths. Applies in both cloud (Azure container name) and local development (subdirectory name). Use path identification extension methods from CMS.IO.Extensions (for example, IsMediaLibraryPath()) to identify system paths. See System-registered paths for the full list of identification methods. |
using CMS.IO.Extensions;
using Kentico.Xperience.Cloud;
builder.Services.AddXperienceCloudStoragePathMapping(options =>
{
// Set the default container name (default value is "default")
options.ContainerName = "default";
// Use a different container for specific paths
options.ConfigureContainerForPath = (registration, setup) =>
{
if (registration.IsMediaLibraryPath())
{
setup.ContainerName = "media-files";
}
};
});With this setup, media library files are stored in the media-files Azure Blob container in cloud environments and under $StorageAssets/media-files/ in local development. All other paths use the default container (or $StorageAssets/default/ locally).
Changing container names does not migrate existing data
Reconfiguring ContainerName or ConfigureContainerForPath does not automatically move files between containers. Any files already stored under the previous container name remain there and become inaccessible to the application once the mapping changes, which can lead to missing or broken binary data.
Use container names conforming to Azure Blob storage container naming requirements (opens in a new tab).
Containers do not need to have an existing folder in the storage assets root folder, but need to have a valid mapping in your application – Xperience automatically ensures that the folder structure gets created in Azure Blob storage when your application tries to access those folders.
Containers in deployment environments are isolated. You can map folders in different environments to containers with identical names.
If your application stores files in custom directories beyond what modules register, register them before the mapping call. See Register custom paths.
Custom mapping module approach
AddXperienceCloudStoragePathMapping() is a smart default designed to cover standard SaaS deployments. If its options don't meet your requirements – for example, you need advanced per-environment logic, non-standard authentication, or highly specific provider configuration – you can implement a custom mapping module instead. See Custom file system providers for instructions.
Prior to version 31.6.0, SaaS projects configured storage path mapping using a custom StorageInitializationModule class (typically StorageInitializationModule.cs). This module manually created AzureStorageProvider instances, called StorageHelper.MapStoragePath() for each path, and branched on the hosting environment.
This module is replaced by a single AddXperienceCloudStoragePathMapping() call in Program.cs. Platform modules now register their own paths automatically – the application only declares its hosting model. See Storage path mapping for details on how the new system works.
If your project still uses a StorageInitializationModule, migrate to the new approach. For most common cases, the entire module collapses to:
builder.Services.AddXperienceCloudStoragePathMapping();Projects with custom container names, per-path overrides, or custom paths require additional configuration – see Customize the storage path mapping and Register custom paths.
Kentico provides a migrate-storage-module-guide.md as a downloadable Markdown file that can be used as input for an AI coding agent (such as GitHub Copilot, Cursor, or similar). The guide covers all migration scenarios, including complex cases like per-path container overrides and retained modules for inexpressible mappings.
AI-assisted migration limitations
AI-generated code may contain errors. You are responsible for validating all changes and ensuring correctness. All changes must be tested in every deployment environment (local development, QA, UAT, production), as each environment may have different storage configurations.
To map parts of the file system to Azure Blob storage when deploying to private cloud, use the storage path mapping system.
If you need to use HTTP (for example because of storage accounts supporting only unencrypted (HTTP) connections), include the CMSAzureBlobEndPoint configuration key in your application's configuration file. Set the value of the key to the full endpoint URL of the external storage account and explicitly specify the HTTP protocol:
{
...
"CMSAzureBlobEndPoint": "http://_StorageAccountName_.blob.core.windows.net"
}Choose your authentication method. See Authentication methods for Azure Blob storage for detailed configuration options.
Add the Kentico.Xperience.AzureStorage NuGet package to your project.
Call AddAppServiceStoragePathMapping() in Program.cs:
using Kentico.Xperience.AzureStorage;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAppServiceStoragePathMapping(options =>
{
// Disable mapping in local development to avoid connecting to Azure
options.IsMappingEnabled = !builder.Environment.IsDevelopment();
});This maps all registered SharedPersistent paths to Azure Blob Storage (default container: cmsstorage) and SharedTemp paths to a shared file system.
Calling AddAppServiceStoragePathMapping() activates storage mapping unconditionally – there is no automatic environment detection. If you call this method without setting IsMappingEnabled, Azure Blob storage is active in all environments, including local development.
Test your configuration in every environment before going to production. A mapping that compiles correctly may behave differently depending on Azure credentials and container availability.
Custom mapping module approach
AddAppServiceStoragePathMapping() is a smart default designed to cover standard Azure App Service deployments. If its options don't meet your requirements – for example, you need advanced per-environment logic, non-standard authentication, or highly specific provider configuration – you can implement a custom mapping module instead. See Custom file system providers for instructions.
(Optional) Set Optional application settings for Azure storage.
If your application stores files in custom directories, register them before the mapping call. See Register custom paths.
Pass an options callback to AddAppServiceStoragePathMapping() to customize the mapping:
| Property | Type | Default | Description |
|---|---|---|---|
IsMappingEnabled | bool | true | Controls whether storage path mapping is active. Set to false to disable mapping entirely (e.g., in local development). When disabled, all paths use the default local file system provider. |
ContainerName | string | "cmsstorage" | Azure Blob container name used for SharedPersistent paths. |
CreateProviderForPath | Func<PathRegistration, StorageProvider> | null | Callback that overrides the storage provider for specific paths. Return a custom StorageProvider to use for the path, or null to fall back to the default provider for that path type. Use path identification extension methods from CMS.IO.Extensions (for example, IsMediaLibraryPath()) to identify system paths. See System-registered paths for the full list. |
The following example routes media library files and form file attachments to dedicated Azure Blob containers:
using CMS.IO;
using CMS.IO.Extensions;
using Kentico.Xperience.AzureStorage;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAppServiceStoragePathMapping(options =>
{
options.IsMappingEnabled = !builder.Environment.IsDevelopment();
options.ContainerName = "cmsstorage";
options.CreateProviderForPath = (PathRegistration pathRegistration) =>
{
if (pathRegistration.IsMediaLibraryPath())
{
return AzureStorageProvider.Create("media-files");
}
if (pathRegistration.IsBizFormFilesPath())
{
return AzureStorageProvider.Create("form-submissions", publicExternalFolderObject: false);
}
// Return null to use the default provider for other paths
return null;
};
});Key | Description |
|---|---|
CMSAzureTempPath | The system uses the specified folder to store temporary files on a local disk, for example when transferring large files to or from the storage account. If not set, the system creates and uses an ~/AzureTemp directory in the project's root. JSONSample value |
CMSAzureCachePath | Specifies a folder on a local disk where files requested from the storage account are cached. This helps minimize the amount of blob storage operations, which saves time and resources. If not set, the system creates and uses an ~/AzureCache directory in the project's root. JSONSample value |
CMSAzureBlobEndPoint | Sets the endpoint used for the connection to the blob service of the specified storage account. If you wish to use the default endpoint, remove the setting completely from the appropriate files. JSONSample value |
CMSAzurePublicContainer | Indicates if the blob container used to store the application's files is public. If true, it will be possible to access files directly through the URL of the appropriate blob service, for example: https://<StorageAccountName>.blob.core.windows.net/media/imagelibrary/logo.png JSONSample value |
CMSDownloadBlobTimeout | Specifies the timeout interval in minutes for importing files from Azure Blob storage into Xperience. The default is 1.5 minutes. Increase the interval if you encounter problems when importing large files (2GB+). JSONSample value |
For private cloud deployments, Xperience supports two authentication methods for Azure Blob storage connections:
Managed identity benefits
Managed identity authentication provides several security advantages:
Specify the storage account name and primary access key in your application configuration file (appsettings.json by default).
Open Storage accounts.
Select your storage.
Switch to the Access keys tab.
Use the Storage account name and one of the provided access key values.
{
"CMSAzureAccountName": "StorageAccountName",
"CMSAzureSharedKey": "PrimaryAccessKey"
}Configure managed identity authentication using Microsoft Entra ID. This method provides enhanced security by eliminating the need to store access keys.
Set up managed identity in Azure:
Enable managed identity for your Azure resources. See the Azure managed identities documentation (opens in a new tab).
Grant the managed identity appropriate roles and permissions to your storage account:
Microsoft.Storage/storageAccounts/blobServices/containers/getAcl/action (Get Container ACL (opens in a new tab))Microsoft.Storage/storageAccounts/blobServices/containers/setAcl/action (Set Container ACL (opens in a new tab))Configure storage account details in your application configuration:
{
"CMSAzureAccountName": "StorageAccountName"
}Configure managed identity authentication in your application startup:
using Azure.Identity;
using Kentico.Xperience.AzureStorage;
var builder = WebApplication.CreateBuilder(args);
// Configure managed identity for Azure Storage
builder.Services.AddAzureStorageCredential(serviceProvider =>
{
// For user-assigned managed identity, specify the client ID
return new ManagedIdentityCredential("your-user-assigned-identity-client-id");
// For system-assigned managed identity, use default constructor.
// Note that for system-assigned identitites to work the connected
// application must be deployed in Azure as well.
return new ManagedIdentityCredential();
// For DEVELOPMENT environments, you can also return
// DefaultAzureCredential for automatic credential discovery.
// See the Azure documentation for details.
return new DefaultAzureCredential();
});
// ...In private cloud, Xperience projects using Azure Blob storage cache data from the blob storage on the local file system. This cached data is stored in the AzureTemp and AzureCache folders (see Optional application settings for custom path configuration).
When these folders accumulate too much data, the local storage can become full, potentially causing errors in your application.
Xperience by Kentico automatically monitors and cleans the Azure cache folders using a background process. When the total size of the cache folders exceeds a configured threshold, the system deletes cached files starting from the oldest until the folder size is reduced.
The default cleanup threshold is 45 GB. This means the system only starts removing cached files once the combined size of the AzureTemp and AzureCache folders exceeds 45 GB.
The default 45 GB threshold may be too high for hosting environments with limited disk space. If your App Service Plan's total storage limit is below 45 GB, the default threshold can never be reached and cache files keep growing until the disk is full.
Adjust the threshold to a value appropriate for your deployment's storage capacity.
Configure the cleanup threshold using the AzureStorageCleanerOptions class in your application's startup code (Program.cs):
using Kentico.Xperience.AzureStorage;
builder.Services.Configure<AzureStorageCleanerOptions>(options =>
{
// Sets the threshold to 10 GB (value is in bytes)
options.Treshold = 10L * 1024L * 1024L * 1024L;
});Set the Treshold property to a value in bytes that is lower than your hosting environment's available disk space. Leave enough headroom for the application itself and other files on the disk.