DRS evaluates all affinity and anti-affinity rules before generating migration recommendations. Must-rules are mandatory constraints — DRS will never violate them. When two must-rules conflict (e.g., "VM-A must run on Host-1" and "VM-A must be separated from VM-B" but VM-B is also pinned to Host-1), DRS enters a deadlock state. It generates faults instead of recommendations, and VMs cannot be placed or migrated. Should-rules are soft constraints that DRS will violate if necessary — but must-rules always override should-rules.
List all DRS rules and identify conflicts
# PowerCLI — List all DRS rules in the cluster:
$cluster = Get-Cluster "ClusterName"
Get-DrsRule -Cluster $cluster | Format-Table Name, Type, Enabled, Mandatory -AutoSize
# Check VM-Host rules:
Get-DrsVMHostRule -Cluster $cluster | Format-Table Name, Type, Enabled, VMGroup, VMHostGroup -AutoSize
# List VM groups and Host groups:
Get-DrsClusterGroup -Cluster $cluster | Format-Table Name, GroupType, Member -AutoSize
Check DRS faults and failed recommendations
# PowerCLI — Get DRS faults:
Get-DrsRecommendation -Cluster $cluster | Format-Table -AutoSize
# vSphere Client path:
# Cluster → Monitor → vSphere DRS → Recommendations
# Cluster → Monitor → vSphere DRS → Faults
# Check for rule conflicts in events:
Get-VIEvent -Entity $cluster -MaxSamples 100 |
Where { $_.FullFormattedMessage -match "rule|conflict|affinity" } |
Select CreatedTime, FullFormattedMessage | Format-Table -AutoSize
Resolve conflicting rules
# Option 1: Disable conflicting must-rule temporarily:
Get-DrsRule -Cluster $cluster -Name "ConflictingRuleName" |
Set-DrsRule -Enabled:$false
# Option 2: Change must-rule to should-rule:
# vSphere Client → Cluster → Configure → VM/Host Rules
# Edit rule → Change "Must" to "Should"
# Option 3: Remove obsolete rules:
Get-DrsRule -Cluster $cluster -Name "ObsoleteRule" | Remove-DrsRule -Confirm:$false
# Verify DRS health after changes:
Get-Cluster $cluster | Select Name, DrsEnabled, DrsAutomationLevel
Resource pools are designed for resource management, not for organizing VMs like folders. When administrators create deeply nested pools (Dev → Team-A → Project-X → Sprint-1), each level adds reservation overhead. A parent pool with a fixed 32 GB memory reservation locks that memory even if its child VMs only use 8 GB. Meanwhile, VMs in sibling pools starve because the cluster appears fully reserved. The Expandable Reservation checkbox (enabled by default) allows child pools to borrow from the parent — disabling it creates hard silos.
Audit resource pool hierarchy and reservations
# PowerCLI — List all resource pools with reservations:
Get-Cluster "ClusterName" | Get-ResourcePool | Sort Name |
Select Name, Parent,
@{N='CpuReservationMHz';E={$_.CpuReservationMHz}},
@{N='MemReservationGB';E={[math]::Round($_.MemReservationGB,2)}},
@{N='CpuExpandable';E={$_.CpuExpandableReservation}},
@{N='MemExpandable';E={$_.MemExpandableReservation}},
@{N='CpuShares';E={$_.NumCpuShares}},
@{N='MemShares';E={$_.NumMemShares}} |
Format-Table -AutoSize
# Count nesting depth:
Get-ResourcePool | ForEach {
$depth = 0; $p = $_.Parent
while ($p -is [VMware.VimAutomation.ViCore.Types.V1.Inventory.ResourcePool]) {
$depth++; $p = $p.Parent
}
[PSCustomObject]@{ Pool=$_.Name; Depth=$depth }
} | Sort Depth -Descending | Format-Table -AutoSize
Check actual VM resource consumption vs pool reservations
# PowerCLI — Compare pool reservation to actual VM usage:
$pools = Get-Cluster "ClusterName" | Get-ResourcePool
foreach ($pool in $pools) {
$vms = Get-VM -Location $pool -NoRecursion
$usedCpu = ($vms | Measure-Object -Property UsedSpaceGB -Sum).Sum
$usedMem = ($vms | ForEach { $_.MemoryGB }) | Measure-Object -Sum
[PSCustomObject]@{
Pool = $pool.Name
VMCount = $vms.Count
ReservedMemGB = [math]::Round($pool.MemReservationGB,1)
ActualMemGB = [math]::Round($usedMem.Sum,1)
Waste = [math]::Round($pool.MemReservationGB - $usedMem.Sum,1)
}
} | Format-Table -AutoSize
Flatten pool hierarchy and fix shares
# Move VMs out of deeply nested pools:
# Use vSphere Client → Drag VMs to top-level resource pool
# PowerCLI — Reset expandable reservations:
Get-ResourcePool -Name "NestedPool" |
Set-ResourcePool -MemExpandableReservation:$true -CpuExpandableReservation:$true
# Remove empty nested pools:
Get-ResourcePool | Where { (Get-VM -Location $_ -NoRecursion).Count -eq 0 } |
Where { $_.Name -ne "Resources" } |
Remove-ResourcePool -Confirm:$false
# Best practice: Use VM folders for organization, resource pools only for resource control
Check current DRS settings and migration history
# PowerCLI — Check DRS automation level and threshold:
Get-Cluster "ClusterName" | Select Name, DrsEnabled, DrsAutomationLevel,
@{N='MigrationThreshold';E={
$_.ExtensionData.Configuration.DrsConfig.VmotionRate
}}
# Review recent DRS migration events:
Get-VIEvent -Entity (Get-Cluster "ClusterName") -MaxSamples 200 |
Where { $_.FullFormattedMessage -match "DRS|migrat|vMotion" } |
Group { $_.CreatedTime.ToString("yyyy-MM-dd") } |
Select Name, Count | Sort Name
Adjust DRS migration threshold
# PowerCLI — Set DRS to Fully Automated with threshold level 3:
Set-Cluster "ClusterName" -DrsAutomationLevel FullyAutomated -Confirm:$false
# vSphere Client path:
# Cluster → Configure → vSphere DRS → Edit
# Automation Level: Fully Automated
# Migration Threshold slider: Position 3 (default, recommended)
# For performance-sensitive clusters, use Level 2:
# This reduces unnecessary migrations while still balancing significant imbalances
Set per-VM DRS overrides for sensitive workloads
# For latency-sensitive VMs, set individual DRS behavior:
# vSphere Client → Cluster → Configure → VM Overrides → Add
# Select VM → DRS Automation Level: "Partially Automated" or "Manual"
# PowerCLI — Set per-VM DRS override:
$cluster = Get-Cluster "ClusterName"
$vm = Get-VM "LatencySensitiveVM"
$spec = New-Object VMware.Vim.ClusterConfigSpecEx
$vmSpec = New-Object VMware.Vim.ClusterDrsVmConfigSpec
$vmSpec.Operation = "add"
$vmSpec.Info = New-Object VMware.Vim.ClusterDrsVmConfigInfo
$vmSpec.Info.Key = $vm.ExtensionData.MoRef
$vmSpec.Info.Behavior = "partiallyAutomated"
$spec.DrsVmConfigSpec += $vmSpec
$cluster.ExtensionData.ReconfigureComputeResource($spec, $true)
EVC (Enhanced vMotion Compatibility) masks advanced CPU features to create a common baseline across all hosts in a cluster. When EVC is set to "Intel Skylake" baseline, all hosts present the same CPU feature set regardless of their actual hardware generation. If a new host has an older CPU (e.g., Haswell) that lacks features required by the EVC baseline, it cannot join the cluster. Similarly, if EVC is not enabled and hosts have different CPU generations, vMotion fails because the VM's CPU instructions differ between source and destination.
Check current EVC mode and host CPU compatibility
# PowerCLI — Check cluster EVC mode:
Get-Cluster "ClusterName" | Select Name,
@{N='EVCMode';E={$_.ExtensionData.Summary.CurrentEVCModeKey}},
@{N='EVCEnabled';E={$_.EVCMode -ne $null}}
# List host CPU models:
Get-Cluster "ClusterName" | Get-VMHost | Select Name,
@{N='CPUModel';E={$_.ProcessorType}},
@{N='CpuFeatureLevel';E={$_.MaxEVCMode}},
@{N='State';E={$_.ConnectionState}} |
Format-Table -AutoSize
# vSphere Client:
# Cluster → Configure → VMware EVC → View current baseline and compliance
Identify the lowest common CPU denominator
# PowerCLI — Find the lowest EVC mode across all hosts:
$hosts = Get-Cluster "ClusterName" | Get-VMHost
$hosts | Select Name, ProcessorType, MaxEVCMode |
Sort MaxEVCMode | Format-Table -AutoSize
# The EVC baseline must be ≤ the lowest MaxEVCMode in the cluster
# Check if a specific host can join at current EVC level:
# Compare host's MaxEVCMode with cluster's CurrentEVCModeKey
# If host.MaxEVCMode < cluster.EVCMode → host CANNOT join
Fix EVC mode or remove incompatible host
# Option 1: Lower EVC mode to accommodate the oldest CPU
# REQUIREMENT: All VMs must be powered off or migrated first
# vSphere Client → Cluster → Configure → VMware EVC → Change EVC Mode
# Select the lowest common baseline (e.g., "Intel Haswell")
# Option 2: Remove the incompatible host from the cluster
Remove-VMHost -VMHost "incompatible-host.domain.com" -Confirm:$false
# Option 3: Enable EVC on a cluster that has none (all VMs must be off):
Set-Cluster "ClusterName" -EVCMode "intel-broadwell" -Confirm:$false
# Verify after change:
Get-Cluster "ClusterName" | Select Name,
@{N='EVCMode';E={$_.ExtensionData.Summary.CurrentEVCModeKey}}
Create VM Group and Host Group
# PowerCLI — Create a VM group for Oracle VMs:
$cluster = Get-Cluster "ClusterName"
$oracleVMs = Get-VM "OracleDB-01","OracleDB-02","OracleDB-03"
New-DrsClusterGroup -Cluster $cluster -Name "Oracle-VMs" -VM $oracleVMs
# Create a Host group for licensed hosts:
$licensedHosts = Get-VMHost "esxi-01.domain.com","esxi-02.domain.com"
New-DrsClusterGroup -Cluster $cluster -Name "Oracle-Licensed-Hosts" -VMHost $licensedHosts
# Verify groups:
Get-DrsClusterGroup -Cluster $cluster | Format-Table Name, GroupType, Member -AutoSize
Create the VM-Host affinity rule
# PowerCLI — Create "Should run on" rule (recommended for HA safety):
New-DrsVMHostRule -Cluster $cluster -Name "Oracle-Licensing-Affinity" `
-VMGroup "Oracle-VMs" -VMHostGroup "Oracle-Licensed-Hosts" `
-Type ShouldRunOn -Enabled:$true
# For strict licensing compliance, use "Must run on":
# WARNING: If all hosts in the group fail, VMs will NOT be restarted by HA
New-DrsVMHostRule -Cluster $cluster -Name "Oracle-Strict-Licensing" `
-VMGroup "Oracle-VMs" -VMHostGroup "Oracle-Licensed-Hosts" `
-Type MustRunOn -Enabled:$true
# vSphere Client path:
# Cluster → Configure → VM/Host Rules → Add → VM-Host Affinity Rule
Validate HA behavior with affinity rules
# Check that HA can still protect VMs with "Must" rules:
# Ensure the host group has N+1 hosts for HA to work
# PowerCLI — Verify rule compliance:
Get-DrsVMHostRule -Cluster $cluster | Select Name, Type, Enabled,
VMGroup, VMHostGroup | Format-Table -AutoSize
# Simulate host failure impact:
$hostGroup = Get-DrsClusterGroup -Cluster $cluster -Name "Oracle-Licensed-Hosts"
$hostCount = ($hostGroup.Member).Count
Write-Host "Licensed host count: $hostCount"
Write-Host "HA can tolerate $($hostCount - 1) host failure(s) for 'Must' rules"
# If hostCount = 1 and rule is "Must", HA CANNOT protect these VMs!
Identify NUMA topology and check VM placement
# SSH to ESXi — Check NUMA topology:
vsish -e get /hardware/numa/config
# Shows: number of NUMA nodes, cores per node, memory per node
# esxtop — Check VM NUMA placement:
# Press 'm' for memory view
# Look for NHN (NUMA Home Node) column
# VMs should ideally have all vCPUs on the same NHN
# Alternative — sched-stats:
vsish -e get /sched/groups/<vmGroupID>/stats
# Check numa.local and numa.remote memory access counters
# PowerCLI — Get host NUMA info:
Get-VMHost "esxi-01.domain.com" | Select Name,
@{N='NumaNodes';E={$_.ExtensionData.Hardware.NumaInfo.NumNodes}},
@{N='CoresPerNode';E={
$_.NumCpu / $_.ExtensionData.Hardware.NumaInfo.NumNodes
}}
Right-size VMs for NUMA boundaries
# Rule: VM vCPU count ≤ physical cores per NUMA node
# Example: Host with 2 NUMA nodes, 18 cores each → VM max 18 vCPUs for single-node
# Check VMs exceeding NUMA node size:
$hosts = Get-Cluster "ClusterName" | Get-VMHost
foreach ($h in $hosts) {
$coresPerNode = $h.NumCpu / $h.ExtensionData.Hardware.NumaInfo.NumNodes
Get-VM -Location $h | Where { $_.NumCpu -gt $coresPerNode } |
Select Name, NumCpu, @{N='CoresPerNode';E={$coresPerNode}},
@{N='SpansNUMA';E={"YES"}}
} | Format-Table -AutoSize
# Fix: Reduce vCPU count to fit within a single NUMA node
# Or configure cores-per-socket to match NUMA topology:
# VM → Edit Settings → CPU → Cores per Socket = CoresPerNode
Advanced NUMA tuning parameters
# VM Advanced Settings for NUMA optimization:
# VM → Edit Settings → VM Options → Advanced → Configuration Parameters
# Force NUMA node affinity (use with caution):
# numa.nodeAffinity = 0 # Pin to NUMA node 0
# Automatic NUMA sizing (default, recommended):
# numa.autosize = true
# Prefer Hyper-Threading for NUMA scheduling:
# numa.vcpu.preferHT = true # Use HT siblings before spanning nodes
# vNUMA settings (VMs with >8 vCPUs auto-expose vNUMA):
# Set cores-per-socket to match physical NUMA topology
# Example: 16 vCPU VM on host with 18 cores/node
# → Set 1 socket × 16 cores (fits in 1 NUMA node)
# → NOT 2 sockets × 8 cores (forces 2 vNUMA nodes)
numa.autosize handle placement automatically. Only use manual NUMA pinning for extreme latency-sensitive workloads after thorough testing.
ESXi is designed to overcommit memory — the total configured VM memory can exceed physical RAM. This works because VMs rarely use 100% of their allocated memory. ESXi uses a hierarchy of memory reclamation techniques: TPS (Transparent Page Sharing) → Balloon driver → Memory compression → Swapping. Under normal conditions, TPS and ballooning are invisible to VMs. But when too many VMs actively consume memory simultaneously, the host enters progressively worse memory states. Once the host hits "Hard" state, it swaps VM pages to the .vswp file on datastore — this is essentially disk-speed memory access and causes catastrophic performance loss.
Check host memory state and VM ballooning/swapping
# SSH to ESXi — Check memory state:
esxtop
# Press 'm' for memory view
# Key columns:
# MCTLSZ — Balloon size (MB reclaimed by balloon driver)
# SWCUR — Current swap usage (MB swapped to .vswp)
# MEMSZ — Configured memory size
# GRANT — Memory granted to VM
# TCHSZ — Touched (actively used) memory
# Check host-level memory state:
vsish -e get /memory/comprehensive
# Look for: state (high/clear/soft/hard/low)
# PowerCLI — Quick memory audit:
Get-VMHost | Select Name,
@{N='TotalMemGB';E={[math]::Round($_.MemoryTotalGB,1)}},
@{N='UsedMemGB';E={[math]::Round($_.MemoryUsageGB,1)}},
@{N='FreeMemGB';E={[math]::Round($_.MemoryTotalGB - $_.MemoryUsageGB,1)}},
@{N='FreePct';E={[math]::Round(($_.MemoryTotalGB - $_.MemoryUsageGB)/$_.MemoryTotalGB*100,1)}} |
Format-Table -AutoSize
Identify over-provisioned VMs and memory waste
# PowerCLI — Find VMs with large memory that are barely using it:
Get-VM | Where { $_.PowerState -eq "PoweredOn" } | Select Name,
@{N='ConfiguredGB';E={$_.MemoryGB}},
@{N='HostMemUsageGB';E={
[math]::Round(($_.ExtensionData.Summary.QuickStats.HostMemoryUsage)/1024,1)
}},
@{N='BalloonMB';E={$_.ExtensionData.Summary.QuickStats.BalloonedMemory}},
@{N='SwapMB';E={$_.ExtensionData.Summary.QuickStats.SwappedMemory}} |
Sort BalloonMB -Descending | Format-Table -AutoSize
# Find VMs actively being ballooned or swapped:
Get-VM | Where { $_.PowerState -eq "PoweredOn" } | ForEach {
$stats = $_.ExtensionData.Summary.QuickStats
if ($stats.BalloonedMemory -gt 0 -or $stats.SwappedMemory -gt 0) {
[PSCustomObject]@{
VM = $_.Name
BalloonMB = $stats.BalloonedMemory
SwapMB = $stats.SwappedMemory
Host = $_.VMHost.Name
}
}
} | Format-Table -AutoSize
Resolve memory pressure
# Immediate relief:
# 1. vMotion VMs to hosts with available memory
# 2. Power off non-critical VMs
# 3. Reduce memory allocation on over-provisioned VMs
# Long-term fixes:
# Add physical memory to hosts
# Right-size VMs — reduce configured memory to actual usage + 20% headroom
# Remove unnecessary memory reservations:
Get-VM | Where { $_.ExtensionData.ResourceConfig.MemoryAllocation.Reservation -gt 0 } |
Select Name, @{N='MemReservMB';E={
$_.ExtensionData.ResourceConfig.MemoryAllocation.Reservation
}} | Format-Table -AutoSize
# Enable TPS (Transparent Page Sharing) — salted since vSphere 6.0:
# Intra-VM TPS is on by default. Inter-VM TPS requires same salt:
# Host Advanced Setting: Mem.ShareForceSalting = 0 (enables inter-VM TPS)
# WARNING: Security implications — shared pages can be side-channel attacked
# Set memory limits to prevent individual VMs from consuming too much:
# Use resource pools with memory shares instead of per-VM limits
SWCUR > 0 on ANY VM in production, treat it as a P1 incident. Host-level swapping means the VM's memory pages are being served from datastore disk — this is 1000x slower than RAM. Immediately migrate VMs or add resources. Never allow production hosts to remain in "Hard" or "Low" memory state.
Create and configure a Datastore Cluster with SDRS
# vSphere Client:
# Storage view → Right-click Datacenter → New Datastore Cluster
# Enable Storage DRS → Set automation level
# Add datastores to the cluster
# PowerCLI — Check existing datastore cluster configuration:
Get-DatastoreCluster | Select Name,
@{N='SdrsEnabled';E={$_.SdrsAutomationLevel -ne 'Disabled'}},
@{N='AutomationLevel';E={$_.SdrsAutomationLevel}},
@{N='SpaceThreshold';E={$_.SpaceUtilizationThresholdPercent}},
@{N='IOLatencyThresholdMs';E={$_.IOLatencyThresholdMillisecond}},
@{N='DatastoreCount';E={(Get-Datastore -Location $_).Count}} |
Format-Table -AutoSize
# List datastores in the cluster with utilization:
Get-DatastoreCluster "DSCluster-01" | Get-Datastore |
Select Name,
@{N='CapacityGB';E={[math]::Round($_.CapacityGB,1)}},
@{N='FreeGB';E={[math]::Round($_.FreeSpaceGB,1)}},
@{N='UsedPct';E={[math]::Round((1-$_.FreeSpaceGB/$_.CapacityGB)*100,1)}} |
Sort UsedPct -Descending | Format-Table -AutoSize
Tune SDRS thresholds and check recommendations
# PowerCLI — Adjust SDRS settings:
Set-DatastoreCluster "DSCluster-01" -SdrsAutomationLevel FullyAutomated `
-SpaceUtilizationThresholdPercent 80 `
-IOLatencyThresholdMillisecond 15
# Check pending SDRS recommendations:
Get-SdrsRecommendation -DatastoreCluster "DSCluster-01" |
Format-Table -AutoSize
# vSphere Client path:
# Datastore Cluster → Configure → Storage DRS → Edit Settings
# Space: Utilized space threshold (default 80%)
# I/O: I/O latency threshold (default 15ms)
# Advanced: SDRS invocation frequency, No-Recommendation period
Configure SDRS affinity and anti-affinity rules
# VMDK affinity: Keep all disks of a VM on the same datastore
# (Default behavior — recommended for most workloads)
# VMDK anti-affinity: Spread disks across different datastores
# Use for: I/O-intensive VMs that benefit from parallel datastore access
# VM anti-affinity: Keep specific VMs on different datastores
# Use for: HA pairs, replicated VMs
# vSphere Client:
# Datastore Cluster → Configure → Rules → Add
# Types: VMDK Affinity, VMDK Anti-Affinity, VM Anti-Affinity
# Common fix for "SDRS recommendations not applied":
# Check if VMDK affinity rules are blocking the recommendation
# Check if VM has snapshots (SDRS cannot move VMs with snapshots)
# Check if VM disks include RDMs (not supported by SDRS)
DRS runs every 5 minutes and generates recommendations based on cluster load balance analysis. Before recommending a migration, DRS performs a pre-check for each VM: Is vMotion feasible? Does the destination have enough resources? Are there compatibility issues? If the pre-check fails, DRS generates a fault rather than a recommendation. Common blockers include: vMotion VMkernel port group mismatch, connected devices that prevent migration (USB, PCI passthrough), disk space constraints on the target datastore, and per-VM DRS overrides that suppress automation.
Check DRS faults and recommendations
# PowerCLI — Get DRS recommendations and faults:
$cluster = Get-Cluster "ClusterName"
Get-DrsRecommendation -Cluster $cluster | Format-Table -AutoSize
# Check DRS events for fault details:
Get-VIEvent -Entity $cluster -MaxSamples 200 |
Where { $_.FullFormattedMessage -match "DRS|fault|cannot.*migrat|vMotion.*fail" } |
Select CreatedTime, FullFormattedMessage |
Sort CreatedTime -Descending | Select -First 20 | Format-Table -AutoSize
# vSphere Client:
# Cluster → Monitor → vSphere DRS → Recommendations (check for faults)
# Cluster → Monitor → vSphere DRS → Faults (detailed fault list)
Check VM-level DRS overrides and migration blockers
# PowerCLI — Find VMs with DRS overrides:
$cluster = Get-Cluster "ClusterName"
$cluster.ExtensionData.Configuration.DrsVmConfig | ForEach {
$vm = Get-VM -Id ("VirtualMachine-" + $_.Key.Value)
[PSCustomObject]@{
VM = $vm.Name
DRSBehavior = $_.Behavior
Enabled = $_.Enabled
}
} | Format-Table -AutoSize
# Check for connected devices blocking vMotion:
Get-VM | ForEach {
$usb = ($_.ExtensionData.Config.Hardware.Device |
Where { $_ -is [VMware.Vim.VirtualUSB] })
$pci = ($_.ExtensionData.Config.Hardware.Device |
Where { $_ -is [VMware.Vim.VirtualPCIPassthrough] })
if ($usb -or $pci) {
[PSCustomObject]@{
VM = $_.Name; USB = [bool]$usb; PCIPassthrough = [bool]$pci
}
}
} | Format-Table -AutoSize
# Verify vMotion network on all hosts:
Get-VMHost | Get-VMHostNetworkAdapter -VMKernel |
Where { $_.VMotionEnabled } |
Select VMHost, Name, IP, SubnetMask | Format-Table -AutoSize
Resolve DRS faults
# Fix 1: Remove per-VM DRS overrides:
# vSphere Client → Cluster → Configure → VM Overrides
# Remove entries with "Disabled" or "Manual" that shouldn't be there
# Fix 2: Verify vMotion connectivity between all hosts:
# Test vMotion from each host to every other host:
$hosts = Get-Cluster "ClusterName" | Get-VMHost
foreach ($src in $hosts) {
foreach ($dst in $hosts | Where { $_ -ne $src }) {
Write-Host "Testing vMotion: $($src.Name) → $($dst.Name)"
# Manual test: try to migrate a small test VM
}
}
# Fix 3: Disconnect USB devices or PCI passthrough on blocked VMs:
# VM → Edit Settings → Remove USB Controller (if not needed)
# Fix 4: Enable EVC for CPU compatibility (see KB #34)
# Fix 5: Ensure destination datastores have enough free space
# Fix 6: Check and fix vMotion VMkernel port group configuration
# After fixing, force DRS to re-evaluate:
# Right-click Cluster → Run DRS Now (vSphere 7.0+)
Enable latency sensitivity on the VM
# vSphere Client:
# VM → Edit Settings → VM Options → Advanced → Latency Sensitivity
# Set to: "High"
# PowerCLI — Enable latency sensitivity:
$vm = Get-VM "TradingEngine-01"
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.LatencySensitivity = New-Object VMware.Vim.LatencySensitivity
$spec.LatencySensitivity.Level = "high"
$vm.ExtensionData.ReconfigVM($spec)
# Verify:
Get-VM "TradingEngine-01" | Select Name,
@{N='LatencySensitivity';E={
$_.ExtensionData.Config.LatencySensitivity.Level
}}
Configure required memory reservation
# Latency Sensitivity = High REQUIRES full memory reservation
# VM → Edit Settings → Resources → Memory → Reservation = Max
# PowerCLI — Set full memory reservation:
$vm = Get-VM "TradingEngine-01"
$vm | Get-VMResourceConfiguration |
Set-VMResourceConfiguration -MemReservationGB $vm.MemoryGB
# Verify reservation equals configured memory:
Get-VM "TradingEngine-01" | Select Name, MemoryGB,
@{N='MemReservGB';E={
[math]::Round($_.ExtensionData.ResourceConfig.MemoryAllocation.Reservation/1024,1)
}}
# Without full reservation, the VM will fail to power on with:
# "Insufficient resources to satisfy configured failover level"
Additional performance tuning for low-latency VMs
# Combine with NUMA pinning for best results:
# VM → Edit Settings → VM Options → Advanced → Configuration Parameters
# numa.nodeAffinity = 0 # Pin to specific NUMA node
# sched.cpu.affinity = 0-7 # Pin to specific pCPUs (use with caution)
# Network optimization for low latency:
# Use VMXNET3 adapter (lowest virtualization overhead)
# Enable SR-IOV for near-native network performance
# Disable TCP Segmentation Offload if using UDP-heavy workloads
# Storage optimization:
# Use PVSCSI controller for lowest storage latency
# Place VM on local SSD/NVMe datastore if possible
# Enable TRIM/UNMAP for thin-provisioned disks
# Host-level tuning:
# Disable power management: esxcli system settings advanced set -o /Power/CpuPolicy -s "High Performance"
# Disable C-States in BIOS for consistent CPU frequency
# Disable Hyper-Threading if workload is CPU-cache sensitive
# Verify latency improvements with esxtop:
# Check %RDY (should be <1%), %CSTP, and %MLMTD