|
| 1 | +--- |
| 2 | +title: Configure NUMA to Optimize Hyper-V Memory |
| 3 | +description: Learn how to configure NUMA in Hyper-V to optimize memory access and performance of virtual machines running on NUMA-enabled hardware. |
| 4 | +ms.topic: how-to |
| 5 | +ai-usage: ai-assisted |
| 6 | +author: dknappettmsft |
| 7 | +ms.author: daknappe |
| 8 | +ms.date: 07/03/2026 |
| 9 | + |
| 10 | +#customer intent: As a Hyper-V administrator, I want to configure NUMA settings so that virtual machines optimize memory access and improve performance on NUMA-enabled hardware. |
| 11 | +--- |
| 12 | + |
| 13 | +# Configure NUMA in Hyper-V |
| 14 | + |
| 15 | +Non-Uniform Memory Access (NUMA) lets virtual machines (VMs) access memory that's physically closer to the CPU cores they use. This feature improves performance for workloads that are sensitive to memory access latency, such as databases and high-performance computing applications. By configuring the NUMA topology and NUMA spanning for your Hyper-V host and VMs, you control how VMs map to the host's physical NUMA nodes. |
| 16 | + |
| 17 | +This article shows you how to configure NUMA settings on multiprocessor systems. NUMA settings don't apply to single-processor systems, as they don't have multiple NUMA nodes. |
| 18 | + |
| 19 | +To learn more about NUMA in Hyper-V, including changes to the behavior with Windows Server 2025 and Windows 11 24H2, see [NUMA in Hyper-V](non-uniform-memory-access.md). |
| 20 | + |
| 21 | +## Prerequisites |
| 22 | + |
| 23 | +Before you configure NUMA in Hyper-V, ensure that you meet the following prerequisites: |
| 24 | + |
| 25 | +- Administrative access to a Hyper-V host. |
| 26 | + |
| 27 | +- NUMA-enabled hardware. |
| 28 | + |
| 29 | +## Determine the NUMA topology of a Hyper-V host |
| 30 | + |
| 31 | +You can determine the NUMA topology of a Hyper-V host by using the [Get-VMHostNumaNode](/powershell/module/hyper-v/get-vmhostnumanode) PowerShell cmdlet from the [Hyper-V module](/powershell/module/hyper-v/). This cmdlet returns information about the NUMA nodes on the host, including the number of logical cores and memory available on each node. |
| 32 | + |
| 33 | +To view the NUMA topology of a Hyper-V host: |
| 34 | + |
| 35 | +1. On a Hyper-V host, open PowerShell as an administrator. |
| 36 | + |
| 37 | +1. Run the following command: |
| 38 | + |
| 39 | + ```powershell |
| 40 | + Get-VMHostNumaNode |
| 41 | + ``` |
| 42 | + |
| 43 | + Here's an example of the output from the command. If you have multiple NUMA nodes, the output shows multiple entries for `NodeId`, each representing a different NUMA node on the host. |
| 44 | + |
| 45 | + ```output |
| 46 | + NodeId : 0 |
| 47 | + ProcessorsAvailability : {94, 99, 100, 100} |
| 48 | + MemoryAvailable : 3196 |
| 49 | + MemoryTotal : 4093 |
| 50 | + ComputerName : host-01 |
| 51 | + |
| 52 | + NodeId : 1 |
| 53 | + ProcessorsAvailability : {66, 73, 98, 99} |
| 54 | + MemoryAvailable : 2533 |
| 55 | + MemoryTotal : 4096 |
| 56 | + ComputerName : host-01 |
| 57 | + ``` |
| 58 | + |
| 59 | +If you have multiple NUMA nodes, consider how you want to configure NUMA spanning and virtual NUMA topology for your VMs based on the available resources on each node. |
| 60 | + |
| 61 | +## Configure NUMA spanning on a Hyper-V host |
| 62 | + |
| 63 | +You can enable or disable NUMA spanning on a Hyper-V host by using Hyper-V Manager or PowerShell. The setting for NUMA spanning on a Hyper-V host only applies to hosts with multiple NUMA nodes. |
| 64 | + |
| 65 | +Select the relevant tab. |
| 66 | + |
| 67 | +# [Hyper-V Manager](#tab/hyper-v-manager) |
| 68 | + |
| 69 | +To configure NUMA spanning on a Hyper-V host by using Hyper-V Manager: |
| 70 | + |
| 71 | +1. Open Hyper-V Manager and select a Hyper-V host in the left pane. |
| 72 | + |
| 73 | +1. Shut down all VMs on that host. If you had VMs running, restart the host, and then open Hyper-V Manager again. |
| 74 | + |
| 75 | +1. Right-click the Hyper-V host, and then select **Hyper-V Settings**. |
| 76 | + |
| 77 | +1. In the section for **Server**, select **NUMA Spanning**. |
| 78 | + |
| 79 | +1. Check the box **Allow virtual machines to span physical NUMA nodes** to enable NUMA spanning, or uncheck it to disable NUMA spanning. |
| 80 | + |
| 81 | +1. Select **OK** to save the changes, and then restart the Hyper-V Virtual Machine Management Service (`vmms`) from the Services console. Alternatively, you can restart the Hyper-V host. |
| 82 | + |
| 83 | +# [PowerShell](#tab/powershell) |
| 84 | + |
| 85 | +To configure NUMA spanning on a Hyper-V host by using PowerShell: |
| 86 | + |
| 87 | +1. Open PowerShell as an administrator on the Hyper-V host. |
| 88 | + |
| 89 | +1. Run the following command to check the current NUMA spanning setting: |
| 90 | + |
| 91 | + ```powershell |
| 92 | + Get-VMHost | FL NumaSpanningEnabled |
| 93 | + ``` |
| 94 | + |
| 95 | + Here's an example of the output from the command. If NUMA spanning is enabled, the value is `True`. If it's disabled, the value is `False`. |
| 96 | + |
| 97 | + ```output |
| 98 | + NumaSpanningEnabled : True |
| 99 | + ``` |
| 100 | + |
| 101 | +1. To change the value, shut down all VMs on that host. If you had VMs running, restart the host, and then open Hyper-V Manager again. |
| 102 | + |
| 103 | +1. Run one of the following commands to enable or disable NUMA spanning: |
| 104 | + |
| 105 | + - To enable NUMA spanning: |
| 106 | + |
| 107 | + ```powershell |
| 108 | + Set-VMHost -NumaSpanningEnabled $true |
| 109 | + ``` |
| 110 | +
|
| 111 | + - To disable NUMA spanning: |
| 112 | +
|
| 113 | + ```powershell |
| 114 | + Set-VMHost -NumaSpanningEnabled $false |
| 115 | + ``` |
| 116 | +
|
| 117 | +1. Run the command in step 2 again to verify that the setting changed. |
| 118 | +
|
| 119 | +1. Restart the Hyper-V Virtual Machine Management Service (`vmms`) by running the following command. Alternatively, you can restart the Hyper-V host. |
| 120 | +
|
| 121 | + ```powershell |
| 122 | + Restart-Service vmms -Verbose |
| 123 | + ``` |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## Configure virtual NUMA topology on a VM |
| 128 | + |
| 129 | +The virtual NUMA topology lets you specify the number of virtual NUMA nodes, the number of virtual processors per node, and the amount of memory to allocate to each node. By default, a VM uses the topology of the physical host. You can configure the virtual NUMA topology for a VM by using Hyper-V Manager or PowerShell. |
| 130 | + |
| 131 | +Select the relevant tab. |
| 132 | + |
| 133 | +# [Hyper-V Manager](#tab/hyper-v-manager) |
| 134 | + |
| 135 | +To configure the virtual NUMA topology for a VM by using Hyper-V Manager: |
| 136 | + |
| 137 | +1. Open Hyper-V Manager and select the VM you want to configure. If the VM is running, shut it down. |
| 138 | + |
| 139 | +1. Right-click the VM, and then select **Settings**. |
| 140 | + |
| 141 | +1. In the **Hardware** section, expand **Processor**, and then select **NUMA**. |
| 142 | + |
| 143 | +1. Set values for the maximum number of processors and memory allowed on a single virtual NUMA node. Change the values based on your requirements. Set the following options: |
| 144 | + |
| 145 | + - **Maximum number of processors** |
| 146 | + - **Maximum amount of memory (MB)** |
| 147 | + - **Maximum NUMA nodes allowed on a socket** |
| 148 | + - **Hardware threads per core** |
| 149 | + |
| 150 | + To reset the values to the default settings, select **Use Hardware Topology**. |
| 151 | + |
| 152 | +1. Select **OK** to save the changes. |
| 153 | + |
| 154 | +# [PowerShell](#tab/powershell) |
| 155 | + |
| 156 | +To configure the virtual NUMA topology for a VM by using PowerShell, replace the placeholder `<values>` with your own values. |
| 157 | + |
| 158 | +1. Open PowerShell as an administrator on the Hyper-V host. |
| 159 | + |
| 160 | +1. If the VM is running, shut it down by running the following command: |
| 161 | + |
| 162 | + ```powershell |
| 163 | + Stop-VM -Name '<VMName>' |
| 164 | + ``` |
| 165 | + |
| 166 | +1. Run the following commands to view the current virtual NUMA topology of the VM: |
| 167 | + |
| 168 | + ```powershell |
| 169 | + $vmName = '<VMName>' |
| 170 | +
|
| 171 | + $vmNuma = @{ |
| 172 | + "Maximum number of processors" = (Get-VMProcessor -VMName $vmName).MaximumCountPerNumaNode |
| 173 | + "Maximum amount of memory (MB)" = (Get-VMMemory -VMName $vmName).MaximumPerNumaNode |
| 174 | + "Maximum NUMA nodes allowed on a socket" = (Get-VMProcessor -VMName $vmName).MaximumCountPerNumaSocket |
| 175 | + "Hardware threads per core" = (Get-VMProcessor -VMName $vmName).HwThreadCountPerCore |
| 176 | + } |
| 177 | +
|
| 178 | + $vmNuma | FT -AutoSize |
| 179 | + ``` |
| 180 | + |
| 181 | + Here's an example of the output from the commands: |
| 182 | + |
| 183 | + ```output |
| 184 | + Name Value |
| 185 | + ---- ----- |
| 186 | + Maximum NUMA nodes allowed on a socket 2 |
| 187 | + Hardware threads per core 0 |
| 188 | + Maximum number of processors 16 |
| 189 | + Maximum amount of memory (MB) 262144 |
| 190 | + ``` |
| 191 | + |
| 192 | +1. Use the following commands to set the virtual NUMA topology for the VM. |
| 193 | + |
| 194 | + - To set the maximum number of processors per NUMA node: |
| 195 | + |
| 196 | + ```powershell |
| 197 | + Set-VMProcessor -VMName '<VMName>' -MaximumCountPerNumaNode '<value>' |
| 198 | + ``` |
| 199 | +
|
| 200 | + - To set the maximum amount of memory per NUMA node (in MB): |
| 201 | +
|
| 202 | + ```powershell |
| 203 | + Set-VMMemory -VMName '<VMName>' -MaximumPerNumaNode '<value>' |
| 204 | + ``` |
| 205 | +
|
| 206 | + - To set the maximum number of processors per NUMA socket: |
| 207 | +
|
| 208 | + ```powershell |
| 209 | + Set-VMProcessor -VMName '<VMName>' -MaximumCountPerNumaSocket '<value>' |
| 210 | + ``` |
| 211 | +
|
| 212 | + - To set the number of hardware threads per core: |
| 213 | +
|
| 214 | + ```powershell |
| 215 | + Set-VMProcessor -VMName '<VMName>' -HwThreadCountPerCore '<value>' |
| 216 | + ``` |
| 217 | +
|
| 218 | +1. Run the commands in step 3 again to verify the updated virtual NUMA topology. |
| 219 | +
|
| 220 | +--- |
| 221 | +
|
| 222 | +## Check VM to NUMA node mapping |
| 223 | +
|
| 224 | +Use the following PowerShell script to determine the mapping of virtual machines to physical NUMA nodes, including the amount of memory allocated to each VM and the remaining memory on each physical NUMA node. |
| 225 | +
|
| 226 | +Running the script as-is returns the mapping for all VMs on the host. You can also specify a VM name to get the mapping for a specific VM by replacing the `*` in the `$vmName` variable with the name of the VM you want to check. |
| 227 | +
|
| 228 | +```powershell |
| 229 | +$vmName = "*" |
| 230 | +
|
| 231 | +if ($vmName -eq "*") { |
| 232 | + $vms = Get-WmiObject -Namespace 'root\virtualization\v2' -Class Msvm_ComputerSystem | Where-Object { $_.ElementName -ne $_.__SERVER } |
| 233 | +} else { |
| 234 | + $vms = Get-WmiObject -Namespace 'root\virtualization\v2' -Class Msvm_ComputerSystem | Where-Object { $_.ElementName -eq $VmName } |
| 235 | +} |
| 236 | +
|
| 237 | +if (!$vms) { |
| 238 | + throw "$vmName does not exist on $($ENV:COMPUTERNAME)" |
| 239 | +} |
| 240 | +
|
| 241 | +$results = @() |
| 242 | +foreach ($vm in $vms) { |
| 243 | + $vmMem = Get-WmiObject -Namespace 'root\virtualization\v2' -Class Msvm_Memory | Where-Object { $_.Name.Contains($vm.Name) } |
| 244 | + $vmMemStg = Get-WmiObject -Namespace 'root\virtualization\v2' -Class Msvm_MemorySettingData | Where-Object { $_.__PATH.Contains($vm.Name) } |
| 245 | + |
| 246 | + if ($vm.EnabledState -ne 2) { |
| 247 | + $results += [PSCustomObject]@{ |
| 248 | + "VM Name" = $vm.ElementName |
| 249 | + "VM Memory" = if ($vmMemStg -is [Array]) { "$($vmMemStg[0].VirtualQuantity) MB" } else { "$($vmMemStg.VirtualQuantity) MB" } |
| 250 | + "Virtual NUMA Node" = "Not applicable - VM is powered off" |
| 251 | + "Physical NUMA Node" = "N/A" |
| 252 | + "Physical NUMA Node Memory Remaining" = "N/A" |
| 253 | + } |
| 254 | + } else { |
| 255 | + if ($vmMem -is [Array]) { |
| 256 | + foreach ($mem in $vmMem) { |
| 257 | + if (!$mem.DeviceID.EndsWith("Aggregate")) { |
| 258 | + $numaNode = $mem.GetRelated("Msvm_NumaNode") |
| 259 | + $results += [PSCustomObject]@{ |
| 260 | + "VM Name" = $vm.ElementName |
| 261 | + "VM Memory" = "$($mem.NumberOfBlocks) MB" |
| 262 | + "Virtual NUMA Node" = $mem.DeviceID.Split("\")[1] |
| 263 | + "Physical NUMA Node" = if ($numaNode) { $numaNode.NodeId.Split("\")[1] } else { "Multi" } |
| 264 | + "Physical NUMA Node Memory Remaining" = if ($numaNode) { "$($numaNode.CurrentlyConsumableMemoryBlocks) MB" } else { "N/A" } |
| 265 | + } |
| 266 | + } |
| 267 | + } |
| 268 | + } else { |
| 269 | + $numaNode = $vmMem.GetRelated("Msvm_NumaNode") |
| 270 | + $results += [PSCustomObject]@{ |
| 271 | + "VM Name" = $vm.ElementName |
| 272 | + "VM Memory" = if ($numaNode) { "$($vmMem.NumberOfBlocks) MB" } else { if ($vmMemStg.DynamicMemoryEnabled) { "Dynamic" } else { "Multi" } } |
| 273 | + "Virtual NUMA Node" = if ($numaNode) { $vmMem.DeviceID.Split("\")[1] } else { "N/A" } |
| 274 | + "Physical NUMA Node" = if ($numaNode) { $numaNode.NodeId.Split("\")[1] } else { "N/A" } |
| 275 | + "Physical NUMA Node Memory Remaining" = if ($numaNode) { "$($numaNode.CurrentlyConsumableMemoryBlocks) MB" } else { "N/A" } |
| 276 | + } |
| 277 | + } |
| 278 | + } |
| 279 | +} |
| 280 | +
|
| 281 | +$results |
| 282 | +``` |
| 283 | + |
| 284 | +Here's an example of the output from the script: |
| 285 | + |
| 286 | +```output |
| 287 | +VM Name : vm01 |
| 288 | +VM Memory : 6828 MB, 6826 MB, 6826 MB |
| 289 | +Virtual NUMA Node : 0, 1, 2 |
| 290 | +Physical NUMA Node : 3, 1, 2 |
| 291 | +Physical NUMA Node Memory Remaining : 1836 MB, 1840 MB, 1840 MB |
| 292 | +``` |
| 293 | + |
| 294 | +Other virtual machines running on the host dictate which physical NUMA nodes best provide memory. When you enable NUMA spanning, Hyper-V might choose NUMA distributions that don't seem to match the physical host well, but this configuration makes sense in the context of the other running virtual machines. |
| 295 | + |
| 296 | +## Determine performance of NUMA spanning on VMs |
| 297 | + |
| 298 | +Hyper-V provides performance counters that you can use to judge how well a virtual machine's virtual NUMA nodes align with the host's physical NUMA nodes. You can retrieve performance counters information through Performance Monitor or the [Get-Counter](/powershell/module/microsoft.powershell.diagnostics/get-counter) PowerShell cmdlet. |
| 299 | + |
| 300 | +You can view the following counters from the `Hyper-V VM Vid Partition` performance object: |
| 301 | + |
| 302 | +- **Remote Physical Pages**: shows how many of a virtual machine's memory pages the host allocates on a nonaligned physical NUMA node. |
| 303 | + |
| 304 | +- **Physical Pages Allocated**: shows how many memory pages a virtual machine has in total. |
| 305 | + |
| 306 | +If the **Remote Physical Pages** counter is zero, the virtual machine is perfectly aligned. If it's not zero, compare it with the total number of memory pages. The **Physical Pages Allocated** counter enables you to determine the percentage of the virtual machine's memory that spans more than one physical NUMA node. |
| 307 | + |
| 308 | +The following PowerShell command retrieves all the `Hyper-V VM Vid Partition` performance counters and shows the values for VMs and the Hyper-V host: |
| 309 | + |
| 310 | +```powershell |
| 311 | +Get-Counter "\Hyper-V VM VID Partition(*)\*" |
| 312 | +``` |
| 313 | + |
| 314 | +Here's an example of the output from the command: |
| 315 | + |
| 316 | +```output |
| 317 | +Timestamp CounterSamples |
| 318 | +--------- -------------- |
| 319 | +15/07/2025 00:00:00 \\host01\hyper-v vm vid partition(vm01)\remote physical pages : |
| 320 | + 0 |
| 321 | +
|
| 322 | + \\host01\hyper-v vm vid partition(vm01)\preferred numa node index : |
| 323 | + 1 |
| 324 | +
|
| 325 | + \\host01\hyper-v vm vid partition(vm01)\physical pages allocated : |
| 326 | + 526344 |
| 327 | +
|
| 328 | + \\host01\hyper-v vm vid partition(_total)\remote physical pages : |
| 329 | + 0 |
| 330 | +
|
| 331 | + \\host01\hyper-v vm vid partition(_total)\preferred numa node index : |
| 332 | + 1 |
| 333 | +
|
| 334 | + \\host01\hyper-v vm vid partition(_total)\physical pages allocated : |
| 335 | + 526344 |
| 336 | +``` |
| 337 | + |
| 338 | +If a VM spans NUMA nodes and it's already performing well, the workload might not depend heavily on memory access latency, or your host might provide remote memory access latencies that are low enough that the misalignment doesn't cause performance problems. |
| 339 | + |
| 340 | +If the workload isn't performing optimally, and performance counters indicate the host allocates a large proportion of the virtual machine's memory on misaligned physical NUMA nodes, consider the following actions: |
| 341 | + |
| 342 | +- Ensure that the configured NUMA topology of the virtual machine aligns well with the Hyper-V host. Try [resetting the virtual machine's NUMA settings to their default values](#configure-virtual-numa-topology-on-a-vm). |
| 343 | + |
| 344 | +- If you have multiple Hyper-V hosts, consider moving the virtual machine to another host. The distribution of available memory across NUMA nodes on the current host might not be ideal with the memory that the host allocates for the virtual machine. |
| 345 | + |
| 346 | +- Consider [disabling NUMA spanning](#configure-numa-spanning-on-a-hyper-v-host) on the Hyper-V host. |
| 347 | + |
| 348 | +## Related content |
| 349 | + |
| 350 | +- [NUMA in Hyper-V](non-uniform-memory-access.md) |
| 351 | +- [Hyper-V PowerShell module](/powershell/module/hyper-v/) |
0 commit comments