How to Set Up a Windows Server Load Balancer with NLB on Windows Server 2022

Network Load Balancing (NLB) is a built-in Windows Server feature that distributes incoming TCP/UDP traffic across multiple servers in a cluster. It is designed for stateless workloads such as web servers, FTP servers, VPN endpoints, and other internet-facing applications. Unlike failover clustering, NLB operates at the network layer and does not share storage between nodes. This guide covers the complete setup of an NLB cluster on Windows Server 2022 using both the NLB Manager GUI and PowerShell.

Understanding NLB Architecture

NLB works by assigning a virtual IP address (also called the cluster IP) to a group of hosts. All hosts in the cluster listen on this virtual IP simultaneously. When a client connects, the NLB driver on each node independently decides—using a deterministic algorithm based on the source IP and port—which node will handle the connection. No communication between nodes is required for this decision, which makes NLB extremely scalable but also means it has no awareness of application-level state.

NLB supports two primary cluster operation modes: unicast and multicast. In unicast mode, all cluster hosts share the same MAC address for the cluster adapter. This means nodes cannot communicate directly with each other through the cluster adapter—a separate network adapter is required for node-to-node communication. In multicast mode, each host retains its original MAC address but also responds to a multicast MAC address. Multicast avoids the two-NIC limitation but requires a managed switch that supports static ARP entries.

Installing the NLB Feature

NLB must be installed on every node that will participate in the cluster. Use Server Manager or PowerShell to install the feature. Repeat this on all nodes before creating the cluster.

# Install NLB feature on the local server
Install-WindowsFeature NLB -IncludeManagementTools

# Verify installation
Get-WindowsFeature NLB

The -IncludeManagementTools flag installs both the NLB PowerShell module and the NLB Manager GUI (nlbmgr.exe). If you only need the management tools on an admin workstation without NLB running locally, you can install just the tools by adding the RSAT-NLB feature.

# Install NLB management tools only (admin workstation)
Install-WindowsFeature RSAT-NLB

Creating an NLB Cluster with PowerShell

To create a new NLB cluster, you need to know the cluster IP address, subnet mask, and the IP address of the first node. The first node added becomes the initial cluster member. Additional nodes are joined afterward.

# Create a new NLB cluster on the first node
# Run this on NODE1 or remotely targeting NODE1
New-NlbCluster `
    -HostName "NODE1.corp.local" `
    -ClusterPrimaryIP "192.168.1.200" `
    -SubnetMask "255.255.255.0" `
    -InterfaceName "Ethernet" `
    -OperationMode "Multicast"

# The cluster name is separate from the IP
# Set the full Internet name (DNS name) for the cluster
(Get-NlbCluster -HostName NODE1).FullInternetName = "webcluster.corp.local"

After creating the cluster on the first node, add additional nodes using Add-NlbClusterNode. The new node must already have the NLB feature installed and the cluster IP must not be in use on that node.

# Add a second node to the existing cluster
Add-NlbClusterNode `
    -HostName "NODE1.corp.local" `
    -NewNodeName "NODE2.corp.local" `
    -NewNodeInterface "Ethernet"

# Add a third node
Add-NlbClusterNode `
    -HostName "NODE1.corp.local" `
    -NewNodeName "NODE3.corp.local" `
    -NewNodeInterface "Ethernet"

Configuring Port Rules

Port rules define which traffic the cluster handles and how it is load-balanced. By default, NLB creates a single port rule covering all ports (1–65535) for all protocols. For most web deployments you will want to replace this with specific rules for ports 80 and 443.

# Remove the default all-port rule
Get-NlbClusterPortRule -HostName NODE1 | Remove-NlbClusterPortRule -Force

# Add a rule for HTTP (port 80)
Add-NlbClusterPortRule `
    -HostName "NODE1.corp.local" `
    -Protocol "TCP" `
    -StartPort 80 `
    -EndPort 80 `
    -Mode "Multiple" `
    -Affinity "Single"

# Add a rule for HTTPS (port 443)
Add-NlbClusterPortRule `
    -HostName "NODE1.corp.local" `
    -Protocol "TCP" `
    -StartPort 443 `
    -EndPort 443 `
    -Mode "Multiple" `
    -Affinity "Single"

The -Affinity parameter controls session persistence. None means any node can handle any request regardless of source (best for raw throughput). Single routes all connections from the same source IP to the same node (needed for session-based apps). Network uses the source Class C subnet instead of a single IP, useful when clients share a proxy.

Using NLB Manager GUI

The NLB Manager (nlbmgr.exe) provides a graphical interface for managing clusters. Launch it from the Start menu or by running nlbmgr.exe from a Run prompt. To connect to an existing cluster, go to Cluster → Connect To Existing. Enter any node’s IP or hostname and the GUI will discover all cluster members.

From the GUI you can drag nodes to suspend or stop them, view cluster properties, add and edit port rules, and check the status of each node. Right-clicking a node gives options to start, stop, suspend, resume, and drain (which stops the node from accepting new connections while finishing existing ones).

NLB with IIS on Windows Server 2022

When using NLB in front of IIS web servers, the IIS configuration must be identical across all nodes. A common approach is to use a shared network path for content or to deploy content using Web Deploy or a CI/CD pipeline to all nodes simultaneously. Application pools should use the same identity and the same configuration on every node.

# Check IIS is running on all nodes
Invoke-Command -ComputerName NODE1, NODE2, NODE3 -ScriptBlock {
    Get-Service W3SVC | Select-Object Name, Status
}

# Verify the same site exists on all nodes
Invoke-Command -ComputerName NODE1, NODE2, NODE3 -ScriptBlock {
    Import-Module WebAdministration
    Get-WebSite | Select-Object Name, State, PhysicalPath
}

For SSL termination, the same certificate must be installed on all nodes. Export the certificate from the first node including the private key (PFX format) and import it on the others. The certificate thumbprint must match on all nodes for seamless failover.

# Export certificate from NODE1
$cert = Get-ChildItem Cert:LocalMachineMy | Where-Object { $_.Subject -like "*webcluster*" }
Export-PfxCertificate -Cert $cert -FilePath "\sharewebcluster.pfx" -Password (Read-Host -AsSecureString "PFX Password")

# Import on NODE2 and NODE3
Invoke-Command -ComputerName NODE2, NODE3 -ScriptBlock {
    Import-PfxCertificate -FilePath "\sharewebcluster.pfx" `
        -CertStoreLocation Cert:LocalMachineMy `
        -Password (ConvertTo-SecureString "yourpassword" -AsPlainText -Force)
}

Managing NLB with PowerShell

The NLB PowerShell module provides full cluster management capabilities. Common operations include checking cluster status, draining a node before maintenance, and removing a node.

# Get all NLB cluster information
Get-NlbCluster

# Get all nodes in a specific cluster
Get-NlbClusterNode -HostName NODE1

# Get port rules
Get-NlbClusterPortRule -HostName NODE1

# Stop a single node (takes it out of the cluster)
Stop-NlbClusterNode -HostName NODE2 -InterfaceName Ethernet

# Drain a node (finish current connections, accept no new ones)
Suspend-NlbClusterNode -HostName NODE2 -InterfaceName Ethernet -Drain

# Resume a suspended node
Resume-NlbClusterNode -HostName NODE2 -InterfaceName Ethernet

# Remove a node permanently from the cluster
Remove-NlbClusterNode -HostName NODE1 -NodeName NODE3

NLB Limitations and Considerations

NLB is not suitable for every workload. Because each node independently processes incoming traffic without awareness of the others, NLB cannot load-balance traffic that requires shared state without an external session store. Applications that write to a local database, local file system, or in-process session cache will behave incorrectly under NLB unless affinity is enabled—but affinity defeats the purpose of load balancing for large numbers of unique clients.

NLB does not perform health checks. If a node fails, its NLB driver stops responding, and traffic is redistributed automatically over a period of approximately 5 minutes (the default convergence time). This is much slower than a true application load balancer. NLB also does not support HTTPS inspection, path-based routing, or header manipulation—for those features, consider ARR (Application Request Routing) in IIS or an external appliance.

NLB clusters are limited to 32 nodes. Unicast mode requires at minimum two network adapters per node if intra-cluster communication is needed. Hyper-V virtual machines running NLB in unicast mode require MAC address spoofing to be enabled on the virtual switch port.

NLB vs Windows Server Failover Clustering

Windows Server Failover Clustering (WSFC) and NLB serve fundamentally different purposes and should not be confused. NLB is designed for active-active load distribution of stateless TCP/UDP traffic. WSFC is designed for high availability of stateful services—SQL Server, file servers, Hyper-V, Exchange—where only one node owns a resource at a time (active-passive) or where shared storage coordinates access.

WSFC uses heartbeat monitoring between nodes and performs failover in seconds when a node goes down. NLB convergence takes minutes. WSFC requires shared storage (SAN, iSCSI, SMB) or Storage Spaces Direct. NLB has no storage requirements. They can be used together: for example, a WSFC SQL cluster as the backend with an NLB web tier at the front.

# Check if Failover Clustering is also installed (different feature)
Get-WindowsFeature Failover-Clustering

# NLB and WSFC features are completely separate
# NLB = Network Load Balancing
# WSFC = Failover-Clustering

Verifying the NLB Cluster

After setup, verify that the cluster virtual IP is reachable and that traffic is being distributed. Use ping to test basic connectivity to the cluster IP, and use Test-NetConnection to verify port accessibility.

# Test connectivity to the cluster virtual IP
Test-NetConnection -ComputerName 192.168.1.200 -Port 80

# Trace which node responded using the X-Powered-By or a custom header
# (Add a custom header in IIS on each node identifying the server)
for ($i = 1; $i -le 10; $i++) {
    $response = Invoke-WebRequest -Uri "http://192.168.1.200" -UseBasicParsing
    Write-Host "Request $i - Status: $($response.StatusCode)"
}

To verify which node is actually serving requests, add a custom HTTP response header in each node’s IIS configuration containing the server name. Then use Invoke-WebRequest in a loop and inspect the headers to confirm traffic is being distributed across nodes.