FSLogix Cloud Cache on Azure Files Premium — 100k IOPS Real-World Benchmarks

Exact CCDLocations config, redirections.xml exclusion strategy, and the KQL alert queries that stopped profile lock contention before users ever noticed — from a 12,000-user AVD deployment running today.

VDI Guy
Senior EUC Architect · Reno, NV · vdiguy.com

Why Cloud Cache in 2026?

If you're running AVD or Horizon with FSLogix and you're not using Cloud Cache, you have a single point of failure hiding in your profile store. Direct SMB mode is fast when everything works — but one storage hiccup during a logon or logoff, and that VHDX gets corrupted or locked. I've seen it take down hundreds of concurrent sessions in minutes.

Cloud Cache changes the failure model entirely. It writes to local disk first, then asynchronously syncs to multiple remote providers. Users stay productive even when your primary storage is degraded. The local cache absorbs the burst; the network catches up. In theory. In practice, the configuration details matter enormously — which is what this post is actually about.

This is the config I've been running in production for a 12,000-user AVD deployment in a financial services firm. They have two Azure Files Premium shares (East US primary, West US secondary) and strict requirements around data residency and profile integrity. The setup below is what passed their security review and has been stable for 8+ months.

Storage Foundation: Azure Files Premium

Before you touch an FSLogix registry key, your storage needs to be right. Most Cloud Cache problems I've debugged trace back to undersized Azure Files shares.

Share sizing: the IOPS burst math

Azure Files Premium provisions IOPS in a simple formula: 1 IOPS per 1 GiB, up to 100,000 IOPS per share, with 3x burst. For 12,000 users with average VHDX sizes of 4–6 GB:

  • Minimum share size: 10,240 GiB (10 TiB) to get 10,000 baseline IOPS
  • For 100,000 baseline IOPS: 100 TiB share
  • I provision at 20 TiB for this deployment — 20,480 baseline IOPS, 61,440 burst IOPS, enough for logon storms
⚠ Common Mistake

Never size your Azure Files share based on storage capacity alone. A 1 TiB share gives you only 1,024 baseline IOPS — completely inadequate for >200 concurrent logons. Size for IOPS first, storage second.

Share configuration

Azure Files Premium — Required Settings
# Storage Account settings (via Azure Portal or Bicep)
Kind:                  FileStorage
Performance tier:      Premium
Replication:           LRS (local) or ZRS (zone-redundant)
Secure transfer:       Required (TLS 1.2+)
SMB:                   SMB 3.1.1 with AES-256 encryption
Large file shares:     Enabled (required for >5 TiB)

# Share-level access (use Azure RBAC, not legacy ACLs)
Role:                  Storage File Data SMB Share Contributor
Assigned to:           AVD Session Host managed identities
                       OR domain computer accounts (Kerberos)

# NTFS permissions on share root
Creator Owner:         Full Control (this container + objects)
Domain Computers:      Modify (this container + objects)

FSLogix Registry Configuration

Every setting below lives at: HKLM\SOFTWARE\FSLogix\Profiles

Deploy via GPO (Computer Configuration > Preferences > Windows Settings > Registry) or via your Intune configuration profile if you're fully cloud-managed.

Profile Container core settings

Registry — Profile Container Base Config
Enabled                              = 1  (DWORD)
VHDLocations                         = (leave empty — use CCDLocations instead)
VolumeType                           = VHDX  (REG_SZ)
SizeInMBs                            = 30720  (DWORD — 30 GB max, expand with compaction)
IsDynamic                            = 1  (dynamic allocation, not fixed)
ProfileType                          = 0  (Normal — required for Cloud Cache)
DeleteLocalProfileWhenVHDShouldApply = 1  (clears stale local profiles)
FlipFlopProfileDirectoryName         = 1  (Username_SID naming vs SID_Username)
PreventLoginWithFailure              = 0  (allow local profile fallback on CCD failure)
PreventLoginWithTempProfile          = 1  (never allow temp profile — fail login instead)
💡 Production Note

ProfileType must be 0 with Cloud Cache. Using ProfileType 3 (read-only merge-back) with CCD creates a double-overhead nightmare — changes are written to local disk AND synced to all providers. I've seen this cause 3-minute logoffs. Keep it 0.

CCDLocations — the most important setting

This is where I see the most misconfiguration in the field. The order matters: the first location is the read-preferred provider. Writes go to all providers simultaneously. If a provider is unhealthy at logon, FSLogix tries the next one.

Registry — CCDLocations (MULTI_SZ, one entry per line)
# Location 1: Primary — Azure Files Premium (East US)
type=smb,name=PrimaryEastUS,connectionString=\\vdiprimary.file.core.windows.net\profiles

# Location 2: DR — Azure Files Premium (West US)
type=smb,name=DRWestUS,connectionString=\\vdidr.file.core.windows.net\profiles

# Optional Location 3: Azure Blob (for additional redundancy)
# type=azure,name=BlobBackup,connectionString="|azure-storage-sas-token|"

Cloud Cache operational settings

Registry — Cloud Cache Operational Settings
# Path: HKLM\SOFTWARE\FSLogix\Profiles

CacheDirectory                       = C:\ProgramData\FSLogix\Cache  (REG_SZ)
# IMPORTANT: Put this on Premium SSD or Ephemeral disk in AVD!
# Standard HDD will bottleneck your entire logon

ClearCacheOnLogoff                   = 1  (DWORD — delete local cache at logoff)
# Set to 1 in pooled/non-persistent. Set to 0 for personal desktops
# where you want instant logon from local cache

HealthyProvidersRequiredForRegister  = 1  (DWORD — min healthy providers to mount)
# 1 = allow logon even if secondary is down (recommended)
# 2 = require both providers healthy (stricter but may block logins)

HealthyProvidersRequiredForUnregister= 1  (DWORD — min providers for clean logoff)

CcdUnregisterTimeout                 = 300  (DWORD — wait up to 5 min for sync on logoff)
# 0 = wait forever (data safe but logoff hangs)
# 300 = 5 min timeout — good balance for 99% of cases

ODFC Container (Office/OneDrive)

Run a separate ODFC container for Outlook OST, Teams, and OneDrive. This keeps profile container sizes manageable and prevents a 15 GB OST file from slow-mounting on every logon.

Registry Path: HKLM\SOFTWARE\FSLogix\ODFC
Enabled                = 1
VolumeType             = VHDX
SizeInMBs             = 51200  (50 GB — OST files can be large)
IsDynamic             = 1
CCDLocations          = (same as profile CCDLocations above)
IncludeOfficeActivation= 1  (keeps Office license in ODFC)

Redirections.xml — What to Exclude

This is the file that lives in your FSLogix share root (or configurable path) that tells FSLogix what to exclude from the profile container. Get this wrong and you'll either have bloated containers or missing data.

My production redirections.xml for a typical enterprise AVD environment:

Redirections.xml — Production Config (2026)
<?xml version="1.0" encoding="UTF-8"?>
<FrxProfileFolderRedirection ExcludeCommonFolders="0">

  <!-- === EXCLUDE: Browser Caches (huge, ephemeral, rebuild on logon) === -->
  <Excludes>
    <Exclude Copy="0">AppData\Local\Google\Chrome\User Data\Default\Cache</Exclude>
    <Exclude Copy="0">AppData\Local\Google\Chrome\User Data\Default\Code Cache</Exclude>
    <Exclude Copy="0">AppData\Local\Microsoft\Edge\User Data\Default\Cache</Exclude>
    <Exclude Copy="0">AppData\Local\Microsoft\Edge\User Data\Default\Code Cache</Exclude>

    <!-- === EXCLUDE: Teams (now handled separately) === -->
    <Exclude Copy="0">AppData\Local\Packages\MSTeams_8wekyb3d8bbwe</Exclude>
    <Exclude Copy="0">AppData\Local\Microsoft\Teams</Exclude>

    <!-- === EXCLUDE: OneDrive KFM (KFM managed separately) === -->
    <Exclude Copy="0">AppData\Local\Microsoft\OneDrive</Exclude>

    <!-- === EXCLUDE: Windows temp and logs === -->
    <Exclude Copy="0">AppData\Local\Temp</Exclude>
    <Exclude Copy="0">AppData\Local\CrashDumps</Exclude>
    <Exclude Copy="0">AppData\Roaming\Microsoft\Windows\Recent</Exclude>

    <!-- === EXCLUDE: Slack/Zoom local caches === -->
    <Exclude Copy="0">AppData\Roaming\Slack\logs</Exclude>
    <Exclude Copy="0">AppData\Roaming\Slack\Cache</Exclude>
    <Exclude Copy="0">AppData\Roaming\Zoom\bin</Exclude>
  </Excludes>

  <!-- === INCLUDE: Force these into container even if excluded by ExcludeCommonFolders === -->
  <Includes>
    <Include>AppData\Roaming\Microsoft\Signatures</Include>
    <Include>AppData\Roaming\Microsoft\Sticky Notes</Include>
    <Include>AppData\Local\Google\Chrome\User Data\Default\Bookmarks</Include>
  </Includes>

</FrxProfileFolderRedirection>

The Benchmarks (12k-User AVD)

MetricBefore (Direct SMB)After (Cloud Cache)Change
Average logon time (P50)18.2s12.4s↓ 32%
P95 logon time44.8s21.1s↓ 53%
Profile lock incidents/week472↓ 96%
Storage outage impactAll users affectedTransparent (local cache)Eliminated
Average container size7.8 GB4.1 GB↓ 47% (redirections.xml)
frxsvc.exe CPU at logon storm38%22%↓ 42%

The container size reduction from 7.8 → 4.1 GB came entirely from the redirections.xml changes — particularly excluding browser caches and the legacy Teams install directory. Smaller containers = faster mounts = faster logons.

KQL Monitoring: Catching Problems Before Users Notice

These queries run as Log Analytics alert rules. I have them firing at P1 (5-min evaluation) so the NOC sees the issue before it cascades.

Query 1: Profile lock contention alert

Log Analytics KQL — FSLogix Lock Detection
// Fires when FSLogix VHDX handles stay open >3 minutes after session end
Event
| where Source == "frxsvc"
| where EventID in (42, 43, 44, 48)  // VHD lock/unlock/failure events
| where TimeGenerated > ago(15m)
| summarize
    LockEvents = count(),
    AffectedUsers = dcount(ParameterXml)
    by Computer, bin(TimeGenerated, 5m)
| where LockEvents > 3
| order by LockEvents desc

Query 2: frxsvc.exe high CPU across AVD hosts

Log Analytics KQL — frxsvc High CPU
// Catches frxsvc CPU spikes that indicate Cloud Cache sync backlog
Perf
| where ObjectName == "Process"
    and CounterName == "% Processor Time"
    and InstanceName == "frxsvc"
| where TimeGenerated > ago(10m)
| summarize
    AvgCPU = avg(CounterValue),
    MaxCPU = max(CounterValue)
    by Computer
| where AvgCPU > 25  // Alert threshold: 25% avg over 10 min
| order by AvgCPU desc

Query 3: Cloud Cache provider health dashboard

Log Analytics KQL — CCD Provider Health
// Shows Cloud Cache provider connectivity state across all AVD hosts
Event
| where Source == "frxccd"
| where TimeGenerated > ago(1h)
| extend
    ProviderStatus = iff(EventID == 45, "HEALTHY",
                    iff(EventID in (46, 47), "DEGRADED", "FAILED"))
| summarize
    HealthyChecks = countif(ProviderStatus == "HEALTHY"),
    DegradedChecks = countif(ProviderStatus == "DEGRADED"),
    FailedChecks = countif(ProviderStatus == "FAILED")
    by Computer
| where FailedChecks > 0 or DegradedChecks > 2
| project-reorder Computer, FailedChecks, DegradedChecks, HealthyChecks

Common Production Issues & Fixes

SymptomRoot CauseFix
Profile stuck "Pending" after session endfrxsvc retains VHDX handle after forced session terminationStop-Service frxsvc; Start-Service frxsvc on that host. Set DeleteLocalProfileWhenVHDShouldApply=1 to prevent recurrence
Logon time spikes during VM scale-out eventsNew VMs don't have local cache — first logon always goes to storagePre-warm cache via scheduled task on VM init, or use ephemeral OS disk so local cache survives pool resets
CCD provider shows unhealthy on 1 hostKerberos ticket expiry or SMB connection timeout to Azure FilesCheck klist on that host. Verify storage account firewall rules allow that host's egress IP. Re-enable Kerberos via klist purge
Container growing beyond SizeInMBsDynamic VHDX doesn't auto-shrink without compactionSchedule: frx compact-disk -volume-path C:\ProgramData\FSLogix\Cache during off-hours
FSLogix 26.01 CU1 logoff slower than 25.09Known behavior: CU1 adds extra sync verification at unregisterSet CcdUnregisterTimeout=120 — this is a feature, not a bug. CU1 is safer; tune timeout to match your SLA
📋 TL;DR Quick Reference

Always use VHDX + Dynamic + ProfileType 0 with Cloud Cache. Put CacheDirectory on Premium SSD or Ephemeral in AVD. Size Azure Files Premium for IOPS first (1 IOPS/GiB). Run separate ODFC container for Outlook/Teams. Use redirections.xml to exclude browser caches. Monitor with the KQL queries above. Always run the latest FSLogix — as of March 2026, that's 26.01 CU1 (3.26.126.19110).

Questions? Drop them in the comments below or ask the AI Troubleshooter — it knows this entire guide and can triage your specific error codes.