Introduction to Storage Tiering with Storage Spaces on Windows Server 2022
Storage tiering is a feature of Windows Server Storage Spaces that automatically moves frequently accessed data to faster storage (such as SSDs or NVMe drives) and less frequently accessed data to higher-capacity, lower-cost storage (such as HDDs). This provides a best-of-both-worlds approach: you get the performance of flash storage for hot data and the capacity efficiency of spinning disks for cold data, all within a single logical volume that the operating system and applications see as a single unified storage device.
Windows Server 2022 supports storage tiering in both standalone Storage Spaces and clustered Storage Spaces Direct (S2D) configurations. With S2D and ReFS, real-time tiering can move data between tiers in the background without an explicit scheduled job. This tutorial covers creating tiered storage pools, configuring tiered virtual disks, monitoring tier usage, troubleshooting, and understanding the performance characteristics of each configuration.
Storage Tiers Overview
Storage Spaces organises physical drives into tiers based on the media type assigned to each drive. The two primary tiers are:
Performance Tier (also called the Journal tier in some S2D configurations): Uses NVMe or SSD drives. Data is stored here when Windows Storage Spaces determines it is accessed frequently (the default threshold is data accessed more than 5 times per hour over a rolling window). The Performance tier provides dramatically lower latency and higher IOPS compared to spinning media. Typical NVMe drives deliver 500,000+ IOPS at sub-100 microsecond latency.
Capacity Tier: Uses HDD drives. Data that is accessed less frequently, or data explicitly designated for this tier, is stored here. HDDs offer much larger capacity per pound/dollar but with significantly higher latency (typically 5-15ms) and lower IOPS (100-200 IOPS per drive). The capacity tier is appropriate for backups, archives, virtual machine snapshots, and any workload that is primarily sequential rather than random.
Windows Server 2022 also supports a three-tier configuration using NVMe as a cache tier, SSD as the Performance tier, and HDD as the Capacity tier. In S2D environments, the NVMe tier is used as a write buffer (NVMe Cache) that absorbs write bursts before they are destaged to SSD or HDD tiers.
Identifying Available Drives and Their Media Types
Before creating a tiered storage pool, verify that the system has the correct drive types and that Windows has correctly identified their media types:
Get-PhysicalDisk | Select-Object FriendlyName, MediaType, BusType, Size, HealthStatus, OperationalStatus | Format-Table
The MediaType property will show SSD, HDD, or Unspecified. If a drive shows Unspecified (common with some NVMe drives or enterprise SSDs), you must manually set the media type before creating the pool, otherwise Storage Spaces will not be able to assign the drive to the correct tier:
Set-PhysicalDisk -FriendlyName "NVMe Samsung 980 Pro 1TB" -MediaType SSD
Or by UniqueId if FriendlyName is not unique:
$disk = Get-PhysicalDisk | Where-Object { $_.UniqueId -eq "disk-unique-id-here" }
Set-PhysicalDisk -UniqueId $disk.UniqueId -MediaType SSD
Verify the pool of available (unallocated) physical disks:
Get-PhysicalDisk -CanPool $true | Select-Object FriendlyName, MediaType, Size | Format-Table
Creating a Tiered Storage Pool
A Storage Pool is the collection of physical disks that will be used to create virtual disks. For a tiered storage configuration, the pool must contain both SSD/NVMe drives and HDD drives. Create the pool using all poolable drives:
$poolDisks = Get-PhysicalDisk -CanPool $true
New-StoragePool -FriendlyName "TieredPool01" -StorageSubSystemFriendlyName "Windows Storage*" -PhysicalDisks $poolDisks
Verify the pool was created and check the drive allocation within it:
Get-StoragePool -FriendlyName "TieredPool01" | Get-PhysicalDisk | Select-Object FriendlyName, MediaType, Size, Usage
After pool creation, Storage Spaces will reserve some drives as hot spares depending on the pool size. You can check the hot spare assignment with:
Get-PhysicalDisk -StoragePool (Get-StoragePool -FriendlyName "TieredPool01") | Where-Object { $_.Usage -eq "HotSpare" }
Viewing and Configuring Storage Tier Definitions
After creating the pool, check whether Storage Spaces has automatically created tier definitions:
Get-StorageTier -StoragePool (Get-StoragePool -FriendlyName "TieredPool01") | Select-Object FriendlyName, MediaType, ResiliencySettingName, Size
If no tiers exist (which can happen on fresh pools), create them manually:
$pool = Get-StoragePool -FriendlyName "TieredPool01"
New-StorageTier -StoragePool $pool -FriendlyName "SSDTier" -MediaType SSD -ResiliencySettingName Mirror -NumberOfDataCopies 2
New-StorageTier -StoragePool $pool -FriendlyName "HDDTier" -MediaType HDD -ResiliencySettingName Mirror -NumberOfDataCopies 2
The -ResiliencySettingName parameter specifies the fault tolerance of each tier independently. Mirror writes two or three copies of data across drives. Parity uses parity striping similar to RAID 5/6 and is more space-efficient but has higher write overhead. For the Performance tier (SSD), always use Mirror resiliency to avoid parity write penalties that would negate the SSD speed advantage. For the Capacity tier (HDD) with a large number of drives, Parity can be more space-efficient.
Checking Maximum Tier Sizes
Before creating a tiered virtual disk, determine the maximum usable size available in each tier. This accounts for resiliency overhead:
$pool = Get-StoragePool -FriendlyName "TieredPool01"
$ssdTier = Get-StorageTier -FriendlyName "SSDTier"
$hddTier = Get-StorageTier -FriendlyName "HDDTier"
Get-StorageTierSupportedSize -StorageTier $ssdTier -ResiliencySettingName Mirror | Select-Object TierSizeMin, TierSizeMax, TierSizeDivisor
Get-StorageTierSupportedSize -StorageTier $hddTier -ResiliencySettingName Mirror | Select-Object TierSizeMin, TierSizeMax, TierSizeDivisor
The TierSizeMax value is the maximum size you can request for that tier. The TierSizeDivisor is the alignment granularity — your requested size must be a multiple of this value. Always size your tiers within the reported maximums, otherwise virtual disk creation will fail.
Creating a Tiered Virtual Disk
With the pool and tier definitions in place, create a tiered virtual disk. The -StorageTiers parameter accepts an ordered array of tier objects, and -StorageTierSizes specifies how much of each tier the virtual disk will consume.
$pool = Get-StoragePool -FriendlyName "TieredPool01"
$ssdTier = Get-StorageTier -FriendlyName "SSDTier"
$hddTier = Get-StorageTier -FriendlyName "HDDTier"
New-VirtualDisk -StoragePool $pool `
-FriendlyName "TieredVDisk01" `
-StorageTiers $ssdTier, $hddTier `
-StorageTierSizes 200GB, 2TB `
-ResiliencySettingName Mirror `
-AutoWriteCacheSize `
-AutoNumberOfColumns
The -StorageTierSizes 200GB, 2TB parameter allocates 200 GB from the SSD tier and 2 TB from the HDD tier. The order of tiers in -StorageTiers must match the order of sizes in -StorageTierSizes. -AutoWriteCacheSize allows Windows to automatically determine the write cache size. -AutoNumberOfColumns lets Storage Spaces determine the optimal number of columns (stripes) based on available drives.
Verify the virtual disk was created correctly:
Get-VirtualDisk -FriendlyName "TieredVDisk01" | Select-Object FriendlyName, HealthStatus, OperationalStatus, Size, FootprintOnPool
Initialising and Formatting the Tiered Virtual Disk
After creating the virtual disk, initialise it, create a partition, and format it. For Storage Spaces with tiering, ReFS is the recommended file system because it supports real-time tiering (on S2D) and integrity streams:
$vdisk = Get-VirtualDisk -FriendlyName "TieredVDisk01"
$disk = $vdisk | Get-Disk
Initialize-Disk -Number $disk.Number -PartitionStyle GPT
New-Partition -DiskNumber $disk.Number -UseMaximumSize -AssignDriveLetter | Format-Volume -FileSystem ReFS -NewFileSystemLabel "TieredVolume" -AllocationUnitSize 65536 -Confirm:$false
The 64KB allocation unit size (65536 bytes) is recommended for ReFS on Storage Spaces to align with the storage pool’s stripe width and maximise I/O efficiency.
Manual Tier Assignment for Specific Files
Storage Spaces’ automatic tiering moves data between tiers based on access frequency. However, you can manually force specific files or directories to reside in a particular tier using Move-VirtualDiskStorageTier or the Set-FileStorageTier cmdlet.
To pin a file to the Performance (SSD) tier permanently:
$ssdTier = Get-StorageTier -FriendlyName "SSDTier"
Set-FileStorageTier -FilePath "D:Databasesproduction.mdf" -DesiredStorageTier $ssdTier
To pin a file to the Capacity (HDD) tier (useful for archive or cold backup files that you never want to promote):
$hddTier = Get-StorageTier -FriendlyName "HDDTier"
Set-FileStorageTier -FilePath "D:Archiveold-backups.bak" -DesiredStorageTier $hddTier
To remove a tier pin and return a file to automatic tiering:
Clear-FileStorageTier -FilePath "D:Databasesproduction.mdf"
To view all files with tier assignments on a volume:
Get-FileStorageTier -VolumePath "D:" | Select-Object FilePath, DesiredStorageTierFriendlyName, State | Format-Table
To force an immediate tiering optimisation run (normally runs nightly via the Storage Tiers Optimisation scheduled task):
Optimize-Volume -DriveLetter D -TierOptimize -Verbose
Monitoring Storage Tier Performance
Storage Spaces provides Performance Monitor counters specifically for tier utilisation. To see the current tier allocation for a virtual disk:
Get-StorageTier | Where-Object { $_.VirtualDisk -ne $null } | Select-Object FriendlyName, Size, AllocatedSize, MediaType | Format-Table
To check how much of each tier is currently used:
Get-StorageTier -FriendlyName "SSDTier" | Select-Object FriendlyName, AllocatedSize, Size, @{Name="UsedPct";Expression={[math]::Round($_.AllocatedSize/$_.Size*100,1)}}
To monitor tier performance in real time using Performance Monitor counters, add these counters in PerfMon or collect them with PowerShell:
$counters = @(
"Storage Spaces Tier(*)Read IO/sec",
"Storage Spaces Tier(*)Write IO/sec",
"Storage Spaces Tier(*)Read Throughput/sec",
"Storage Spaces Tier(*)Write Throughput/sec"
)
Get-Counter -Counter $counters -SampleInterval 2 -MaxSamples 10 | ForEach-Object { $_.CounterSamples | Select-Object Path, CookedValue }
Watch for a high ratio of reads on the SSD tier versus the HDD tier — this indicates tiering is working effectively and hot data is being served from flash. If you see high HDD read IOPS on a workload that should be hitting the SSD tier, run an immediate tiering optimisation as the access pattern data may not have yet triggered automatic promotion.
ReFS and Storage Spaces Real-Time Tiering
When using Storage Spaces Direct with ReFS on Windows Server 2022, real-time tiering is available. Unlike the scheduled-task-based tiering used with NTFS and standalone Storage Spaces, ReFS real-time tiering moves data between tiers immediately as access patterns are detected — there is no wait until the nightly scheduled task runs.
Real-time tiering works by maintaining a heat map of all extents (blocks) in the volume. When an extent’s access frequency crosses the promotion threshold, ReFS immediately schedules it for movement to the Performance tier. This dramatically improves performance responsiveness for workloads with rapidly changing hot sets.
To verify that a volume is using ReFS with tiering support:
Get-Volume -DriveLetter D | Select-Object DriveLetter, FileSystem, FileSystemLabel, HealthStatus
Get-VirtualDisk -FriendlyName "TieredVDisk01" | Select-Object FriendlyName, StorageTierFriendlyNames
On S2D clusters, also check the tiering status via:
Get-StorageJob | Where-Object { $_.Name -like "*Tier*" } | Select-Object Name, PercentComplete, JobState
Troubleshooting Storage Tier Issues
If the Storage Tiers Optimisation scheduled task fails to run, check its status and last run result:
Get-ScheduledTask -TaskPath "MicrosoftWindowsStorage Tiers Management" | Select-Object TaskName, State, LastRunTime, LastTaskResult
A LastTaskResult of 0 means success. Non-zero values indicate errors. Re-enable and run the task manually:
Enable-ScheduledTask -TaskPath "MicrosoftWindowsStorage Tiers Management" -TaskName "Storage Tiers Optimization"
Start-ScheduledTask -TaskPath "MicrosoftWindowsStorage Tiers Management" -TaskName "Storage Tiers Optimization"
If a virtual disk shows a degraded or failed tier, check physical disk health:
Get-PhysicalDisk | Where-Object { $_.HealthStatus -ne "Healthy" } | Select-Object FriendlyName, MediaType, HealthStatus, OperationalStatus
If you need to resize a tier after creation (for example, adding more SSD capacity to the pool and extending the SSD tier), first add the new drive to the pool and then resize the tier:
$newDrive = Get-PhysicalDisk -CanPool $true | Where-Object { $_.MediaType -eq "SSD" }
Add-PhysicalDisk -StoragePool (Get-StoragePool "TieredPool01") -PhysicalDisks $newDrive
Resize-StorageTier -FriendlyName "SSDTier" -Size 400GB
If a tiered virtual disk shows HealthStatus as Warning with an OperationalStatus of Incomplete, this typically means a tier optimisation is needed or a physical disk was replaced. Run a repair job:
Get-VirtualDisk -FriendlyName "TieredVDisk01" | Repair-VirtualDisk
Monitor the repair job progress:
Get-StorageJob | Where-Object { $_.JobState -eq "Running" } | Select-Object Name, PercentComplete, JobState, ElapsedTime
Storage tiering with Storage Spaces on Windows Server 2022 provides a transparent and automated way to maximise both storage performance and cost efficiency. By combining NVMe/SSD and HDD drives in a single tiered pool, workloads receive flash-like performance for their active dataset while benefiting from the cost-per-GB efficiency of spinning disk for the remainder — all without any application changes or manual data management.