Opening TCP 135 on a Windows Server firewall does not enable RPC. Port 135 is only the endpoint mapper: the client asks it which port a given interface is listening on, and the answer is a dynamically allocated port somewhere in the 49152–65535 range. Block that range and every RPC call fails with a timeout that looks exactly like a dead server. This page covers how the mapper hands out ports, the registry keys that pin the range, how to scope firewall rules so they follow the service rather than the port, and what breaks when the range is narrowed too far. The same RPC plumbing applies on any hosted guest, including the Windows Server VPS images at windows-vps.org.
Why Port 135 Alone Never Works
An RPC client performs a three-step dance. It connects to the endpoint mapper on TCP 135, sends the interface UUID and version it wants, and receives back a port number. It then opens a second connection to that port, which was chosen at boot from the dynamic range. A firewall that permits 135 and denies 49152–65535 allows the first step and blocks the second, so the error surfaces as RPC server is unavailable after a 30–60 second timeout rather than a clean refusal.
# See what the mapper hands out right now
Get-NetTCPConnection -State Listen | Where-Object {$_.LocalPort -ge 49152} |
Sort-Object LocalPort | Select LocalAddress,LocalPort,OwningProcess |
ForEach-Object { $_ | Add-Member -PassThru NoteProperty Proc (Get-Process -Id $_.OwningProcess).Name }
Well-known RPC services get their own reserved ports. Everything else — DCOM servers, third-party backup agents, custom service hosts — lands in the dynamic range and moves after a reboot unless it is pinned.
| Service | Port | Notes |
|---|---|---|
| Endpoint mapper | 135 TCP | Always required, both directions for cross-server RPC |
| WMI / DCOM (Winmgmt) | 135 + dynamic | WMI over DCOM cannot be pinned; use WinRM instead |
| Netlogon | 445, 135, 49152+ | Domain join and secure channel need the dynamic range |
| DFS Replication | 5722, 135 | Plus dynamic RPC for the management interface |
| Hyper-V management | 2179, 135, dynamic | VMMS uses dynamic ports for the VMI |
| Backup agents (most) | 135 + vendor port | Check vendor docs; many also need 445 |
Pinning the Dynamic Range
Windows Server 2008 and later default the ephemeral range to 49152–65535. Narrowing it to a block you can firewall is done with two registry values under HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters. The format is a 32-bit value where the high 16 bits are the start port and the low 16 bits are the count.
$start = 5000; $count = 1000
$val = ($start -shl 16) -bor $count
Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' -Name TcpipDynamicPortRange -Value $val -Type DWord
# netsh equivalent, same effect, reboot required
netsh int ipv4 set dynamicport tcp start=5000 num=1000
netsh int ipv4 set dynamicport udp start=5000 num=1000
netsh int ipv4 show dynamicport tcp
A range of 1000 ports is comfortable for a single-role server. Do not go below 500: port exhaustion under load produces WSAEADDRINUSE failures in event log and TCP connect errors that have nothing to do with the firewall.
Reserving a specific port for a specific RPC interface is a separate mechanism. Each interface registers under its own key and the Ports subkey, with a Ports REG_MULTI_SZ naming the interfaces:
$k = 'HKLM:\SOFTWARE\Microsoft\Rpc\Internet'
New-Item $k -Force | Out-Null
New-ItemProperty $k -Name Ports -PropertyType MultiString -Value @('5000-5999') -Force
New-ItemProperty $k -Name PortsInternetAvailable -Value 'Y' -PropertyType String -Force
New-ItemProperty $k -Name UseInternetPorts -Value 'Y' -PropertyType String -Force
This key is the one most documentation refers to, and it must agree with the Tcpip value — a mismatch causes the service to fall back to a random port outside both ranges.
Scoping Firewall Rules to the Service, Not the Range
The cleaner approach on modern Windows is to let the firewall follow the process. A rule with -Program and a dynamic local port matches only traffic owned by that executable, which means the dynamic range can stay open on the local side without exposing every RPC server on the box.
New-NetFirewallRule -DisplayName 'RPC - Backup Agent' -Direction Inbound -Action Allow `
-Protocol TCP -LocalPort 135,49152-65535 -RemoteAddress 10.20.0.0/24 `
-Program 'C:\Program Files\Vendor\agent.exe' -Profile Domain,Private
New-NetFirewallRule -DisplayName 'RPC - Admin subnet' -Direction Inbound -Action Allow `
-Protocol TCP -LocalPort 135 -RemoteAddress 10.20.0.0/24 -Profile Domain,Private
Restrict by -RemoteAddress before you restrict by port. The majority of RPC exposure on a VPS is not a port range problem, it is that 135 and 445 are reachable from the public internet. Scoping to the management subnet removes the risk without touching the range at all.
- Use
-Profile Domain,Privateso the rules do not apply to a Public-classified NIC. - Prefer
-InterfaceAliasover IP ranges when the box has one NIC and one management path. - Set
-EdgeTraversalPolicy Blockso no rule is silently opened by NAT traversal. - Group rules with
-GroupsoGet-NetFirewallRule -Groupcan audit them as a unit.
What Breaks When the Range Is Over-Restricted
Each of these failures presents differently, and none of them names RPC in the error message.
| Symptom | What is actually failing | Event evidence |
|---|---|---|
| Domain join fails with “RPC server unavailable” | Netlogon secure channel needs dynamic RPC to the DC | Netlogon 5719, 5783 |
| WMI queries time out but WinRM works | DCOM cannot reach the dynamic port | DistributedCOM 10009 |
| Backup job fails at “snapshot creation” | VSS writer RPC call blocked | VSS 8194, Application log |
| GPO not applying | Group Policy client RPC to SYSVOL | GroupPolicy 1058, 1129 |
| Server Manager cannot add a role remotely | WinRM fine, but the role install uses DCOM | ServerManager 0 |
The domain join case is the one that locks people out of a fresh VPS rebuild: the machine can reach the DC on 389 and 445 and still fail to join because the Netlogon RPC call to the DC’s dynamic port was dropped. If you must narrow the range, keep a 2000-port block open between member servers and domain controllers and log the drops during the first reboot cycle.
Verifying What Is Actually Listening
Run the verification below before and after the change window and diff the two outputs. A port that appears in the listener list but has no matching allow rule is a service that will fail silently the next time it restarts.
Do not trust the rule list alone. Match listening sockets to processes, then match processes to rules. A listener on a port outside your allowed range is the thing to find.
# Listening RPC endpoints with owning process and command line
Get-NetTCPConnection -State Listen | Where-Object {$_.LocalPort -in 135 -or $_.LocalPort -ge 49152} |
ForEach-Object {
$p = Get-CimInstance Win32_Process -Filter "ProcessId=$($_.OwningProcess)"
[pscustomobject]@{Port=$_.LocalPort; Proc=$p.Name; Path=$p.ExecutablePath}
} | Sort-Object Port | Format-Table -AutoSize
# Which inbound rules reference each port
Get-NetFirewallPortFilter | Where-Object {$_.LocalPort -match '49152'} |
ForEach-Object { $_ | Get-NetFirewallRule } | Select DisplayName,Enabled,Direction,Action
# Effective profile on each interface
Get-NetConnectionProfile | Select InterfaceAlias,NetworkCategory,IPv4Connectivity
Then watch the drops during a change window instead of guessing from user reports:
# Enable the firewall drop log, then tail it
netsh advfirewall set allprofiles logging droppedconnections enable
netsh advfirewall set allprofiles logging maxfilesize 8192
Get-Content "$env:SystemRoot\System32\LogFiles\Firewall\pfirewall.log" -Tail 40
Pair that with the Security log filter for 5152 (packet blocked) and 5157, and with the DistributedCOM 10009 entries that carry the CLSID and remote address. A 15-minute capture during a backup run or a GPO refresh tells you exactly which port a service needed, which is far faster than reading vendor documentation.
A Rollout Order That Does Not Lock You Out
Two additional checks belong in any change window: confirm the DNS and WMI providers still answer from the management subnet, and keep a copy of the previous rule set so a bad call can be undone in one command.
Ordering the Change Window
Change the range and the rules in this order, and keep an out-of-band console open for the whole window.
- Enable drop logging first, with the existing permissive rules. Collect a baseline for 24 hours.
- Add the new scoped rules alongside the old ones; both are active, nothing breaks.
- Reboot to confirm the pinned range is in effect:
netsh int ipv4 show dynamicport tcp. - Remove the permissive rules one group at a time, watching the drop log for 10 minutes after each.
- Reboot again and re-run the service checks — some RPC endpoints bind at boot before the range is read, and a second boot is where range misconfiguration shows up.
If the guest becomes unreachable during the window, the provider console is the rollback: re-enable the permissive rule set with netsh advfirewall set allprofiles state off only as a last resort, then fix and re-enable. Keeping the baseline drop log means you will know which rule to add rather than guessing. Current Windows Server builds and their default network posture are described on the Windows VPS product page.


