Azure Blob storage

Xperience by Kentico supports file system providers that allow you to map parts of the file system to Microsoft Azure Blob Storage. 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 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 library files in Azure Blob storage

Media libraries sunset

Media libraries have been officially sunset. Support for media libraries will continue for one more year (until July 24, 2026), after which the feature and all associated APIs will be completely removed.

Before mapping media library files to the Microsoft Azure Blob Storage, 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:

  • Storing a large number (thousands) of media library files in a single media library can significantly affect the performance and user experience of the Media libraries application.
    • We recommend structuring media library files into multiple media libraries and storing at most 100 files in a single media library folder.
  • Mapping subfolders of media libraries is not supported. You can map either the directory containing the media libraries (~/assets/media), or individual media libraries (~/assets/media/<MediaLibraryName>).
  • The system’s automatic clearing of files from the server-side cache does not work for files stored in an external storage. If you modify a media file, the website may still display the old version until the cache expires (unless you manually clear the application’s cache). See also: File caching.

Azure Blob storage for Kentico’s SaaS

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 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.

  • Media library metadata is stored in the Xperience database, only the media library (binary) files are deployed.
  • Files stored in the content item asset fields do not need to be deployed as they are handled by CI/CD.

Default Kentico-managed Azure Blob storage configuration

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:

  • Cloud environments (QA, UAT, Custom, Staging, Production) – all 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.
  • Local development – media library files are mapped to a local directory (~/$StorageAssets/default/assets/media) so they can be included in the deployment package.

All system paths are registered automatically by Xperience modules. See System-registered paths for the full list.

Customize the storage path mapping

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.

C#
Program.cs – custom container configuration


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.

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.

Migrate from StorageInitializationModule

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:

C#
Program.cs


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.

Azure Blob storage for private cloud deployments

To map parts of the file system to Azure Blob storage when deploying to private cloud, use the storage path mapping system.

Recommendations

  1. Use separate Azure Blob storage containers for each deployment environment for the same project (for example, production and testing environments). Such environments often contain identically named files that overwrite each other. To avoid collisions, use different container names per environment.
  2. Use HTTPS to connect to Azure Blob Storage accounts (this behavior can be enabled via the Security transfer required setting available in the Azure portal). Xperience is by default configured to use HTTPS with Azure Blob Storage.
    • 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:

      JSON
      appsettings.json
      
      
        {
        ...
      
            "CMSAzureBlobEndPoint": "http://_StorageAccountName_.blob.core.windows.net"
        }
      
        

Set up storage mapping

  1. Choose your authentication method. See Authentication methods for Azure Blob storage for detailed configuration options.

  2. Add the Kentico.Xperience.AzureStorage NuGet package to your project.

  3. Call AddAppServiceStoragePathMapping() in Program.cs:

    C#
    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.

  4. (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.

Configure private cloud mapping options

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:

C#
Program.cs – per-path container overrides


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;
    };
});

Optional application settings for Azure storage

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.

JSON
Sample value


"CMSAzureTempPath": "C:\\AzureTemp"

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.

JSON
Sample value


"CMSAzureCachePath": "C:\\AzureCache"

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.

JSON
Sample value


"CMSAzureBlobEndPoint": "http://127.0.0.1:10000/devaccount"

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

JSON
Sample value


"CMSAzurePublicContainer": true

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+).

JSON
Sample value


"CMSDownloadBlobTimeout": "5"

Authentication methods for Azure Blob storage

For private cloud deployments, Xperience supports two authentication methods for Azure Blob storage connections:

  • Shared key authentication – Uses the storage account name and access key.
  • Managed identity authentication – Uses Microsoft Entra authentication with managed identities . This is the recommended approach for enhanced security as it eliminates the need to store sensitive access keys in your application configuration.

Managed identity benefits

Managed identity authentication provides several security advantages:

  • No need to store storage account keys in your application configuration
  • Automatic credential rotation handled by Azure
  • Fine-grained access control using Azure RBAC (Role-Based Access Control)
  • Reduced risk of credential exposure

Shared key authentication

Specify the storage account name and primary access key in your application configuration file (appsettings.json by default).

  1. Open the Azure Management Portal.

  2. Open Storage accounts.

  3. Select your storage.

  4. Switch to the Access keys tab.

  5. Use the Storage account name and one of the provided access key values.

    JSON
    appsettings.json
    
    
     {
         "CMSAzureAccountName": "StorageAccountName",
         "CMSAzureSharedKey": "PrimaryAccessKey"
     }
    
     

Managed identity authentication

Configure managed identity authentication using Microsoft Entra ID. This method provides enhanced security by eliminating the need to store access keys.

  1. Set up managed identity in Azure:

    1. Enable managed identity for your Azure resources. See the Azure managed identities documentation.

    2. Grant the managed identity appropriate roles and permissions to your storage account:

      • Role: Storage Blob Data Owner
      • This built-in role includes all necessary permissions for Xperience Azure Storage operations.
      • —OR—
      • Role: Storage Blob Data Contributor
      • With additional permissions:
        • Microsoft.Storage/storageAccounts/blobServices/containers/getAcl/action (Get Container ACL)
        • Microsoft.Storage/storageAccounts/blobServices/containers/setAcl/action (Set Container ACL)
      • The Storage Blob Data Contributor role provides blob read/write access but requires additional container ACL permissions for full functionality. Use this configuration to enforce the principle of least privilege.
  2. Configure storage account details in your application configuration:

    JSON
    appsettings.json
    
     {
         "CMSAzureAccountName": "StorageAccountName"
     }
     
  3. Configure managed identity authentication in your application startup:

    C#
    Program.cs
    
    
     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();
     });
    
     // ...
    
     

Prevent Azure cache folders from bloating

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

Configure the cleanup threshold using the AzureStorageCleanerOptions class in your application’s startup code (Program.cs):

C#
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.