This tutorial goes through the process of setting up a Wireguard server on Windows. Most Wireguard tutorials on the internet only give you the choice of hosting a server in a Linux environment. However, it is very possible to setup a windows server.
After searching for a tutorial to no avail, I spent a couple days to figure out the best way to do it and how to automate the process. Ideally you would still want to run it in an Linux environment, but if you have a use case for a windows server like me, you would appreciate just how flexible Wireguard is!
Prerequisite
- Latest Wireguard Windows Client installed (Download here from official site)
- Setup firewall rules (just as you would for a Linux server setup: open and forward ports 51820, configure ddns etc)
Disclaimer
Using Wireguard on Windows as server is not officially supported. Use at your own risk.
Step 1: Prepare Wireguard Server and Client Config File
This step is the same as other Linux tutorials.
I’ve provided my own server side and client side configs below, adjust to your own use case.
#Server Config
[Interface]
PrivateKey = #Replace with server private key#
ListenPort = 51820
Address = 192.168.200.1/24
[Peer]
#Client 1
PublicKey = #Replace with client public key#
PresharedKey = #Replace with pre-shared key#
AllowedIPs = 192.168.200.2/32
#Client Config
[Interface]
PrivateKey = #Replace with client private key#
Address = 192.168.200.2/24
DNS = 1.1.1.1, 8.8.8.8
[Peer]
PublicKey = #Replace with server public key#
PresharedKey = #Replace with pre-shared key#
AllowedIPs = 0.0.0.0/0
Endpoint = #Replace with server domain name or ip address#:51820
After you prepared the server config files, place it in a folder somewhere permanent.
For this tutorial I will assume the server config file is placed at C:\wireguard\wg_server.conf
Step 2: Start up the server
Instead of using the GUI to start the server, we will start it using command options.
At the time of this tutorial the official GUI only allows one connection at a time. If we use it to run the server, the GUI will be occupied and we won’t be able to make a new connection without dropping the server interface.
Running the server using command line options allows us the keep the GUI free for daily use.
If you don’t mind the GUI being occupied, you can just start the server on the GUI and skip to Step 3.
Use the following code to start / stop the server. Without saying, adjust the file paths if they are different on your system.
You need to run these with administrative privilege!
#Start server
C:\Program Files\WireGuard\wireguard.exe /installtunnelservice "C:\wireguard\wg_server.conf"
#Stop server
C:\Program Files\WireGuard\wireguard.exe /uninstalltunnelservice wg_server
You will only need to run the command once, wireguard’s background service will remember the run state over reboots.
Once you start the server, wireguard will create a new network adapter as the same name as your server config file. Thus for our tutorial, the network adapter name would be “wg_server”
Check if the network adapter is successfully created.

If you named your config file “Wireguard_Server.conf”, the network adapter created will also be name accordingly
Step 2.1: (Optional) Setting adapter profile
Now we have the wireguard adpater setup, it is recommended to change it to “Private” profile”, by defaults the adapter is added as “Public”. Private profile will allow greater compatibility for the clients (say you want to use some remote desktop etc). Private profile may block these ports and services.
To Do this we run three simple powershell commands with admin privilege manually:
#Open powershell with admin privilege and run the following:
$NetworkProfile = Get-NetConnectionProfile -InterfaceAlias "wg_server"
$NetworkProfile.NetworkCategory = "Private"
Set-NetConnectionProfile -InputObject $NetworkProfile
Step 3: Enable server routing
Now that server is running, the client should be able to handshake (given that you have the correct ports open and forwarded correctly).
However, you will notice the client won’t be able to access either the internet or the LAN network.
This is because by default windows do not bridge or NAT the wireguard interface with your actual physical internet interface.
In Linux this is done by some PostUp/PostDown firewall commands, which we do not use here.
Instead, we use a powershell script to enable the NAT (or in Windows term “internet sharing”) function:
Function Set-NetConnectionSharing
{
Param
(
[Parameter(Mandatory=$true)]
[string]
$LocalConnection,
[Parameter(Mandatory=$true)]
[bool]
$Enabled
)
Begin
{
$netShare = $null
try
{
# Create a NetSharingManager object
$netShare = New-Object -ComObject HNetCfg.HNetShare
}
catch
{
# Register the HNetCfg library (once)
regsvr32 /s hnetcfg.dll
# Create a NetSharingManager object
$netShare = New-Object -ComObject HNetCfg.HNetShare
}
}
Process
{
#Clear Existing Share
$oldConnections = $netShare.EnumEveryConnection |? { $netShare.INetSharingConfigurationForINetConnection.Invoke($_).SharingEnabled -eq $true}
foreach($oldShared in $oldConnections)
{
$oldConfig = $netShare.INetSharingConfigurationForINetConnection.Invoke($oldShared)
$oldConfig.DisableSharing()
}
# Find connections
$InternetConnection = Get-NetRoute | ? DestinationPrefix -eq '0.0.0.0/0' | Get-NetIPInterface | Where ConnectionState -eq 'Connected'
$publicConnection = $netShare.EnumEveryConnection |? { $netShare.NetConnectionProps.Invoke($_).Name -eq $InternetConnection.InterfaceAlias }
$privateConnection = $netShare.EnumEveryConnection |? { $netShare.NetConnectionProps.Invoke($_).Name -eq $LocalConnection }
# Get sharing configuration
$publicConfig = $netShare.INetSharingConfigurationForINetConnection.Invoke($publicConnection)
$privateConfig = $netShare.INetSharingConfigurationForINetConnection.Invoke($privateConnection)
if ($Enabled)
{
$publicConfig.EnableSharing(0)
$privateConfig.EnableSharing(1)
}
else
{
$publicConfig.DisableSharing()
$privateConfig.DisableSharing()
}
}
}
Note: The shell script is originally created by igoravl, I made some modification to simplify the process and get rid of some errors for our wireguard server application.
This shell script is written as a custom function “Set-NetConnectionSharing” and needs to be loaded in powershell.
Save the script in the following location:
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\wireguard\wireguard.psm1
wireguard.psm1 needs to be in a folder named wireguard for the function to be loaded by powershell
Now you can open a powershell window with administrative privilege and run the following commands to enable / disable NAT for our wireguard server interface.
#"wg_server" is the wireguard adapter name, replace it if you have something different.
#Enable NAT
Set-NetConnectionSharing "wg_server" $true
#Disable NAT
Set-NetConnectionSharing "wg_server" $false
If everything goes well, when you open the properties panel of your main internet network adaptor (Ethernet 3 in my case) the following options should be ticked:

Notice also the “Home networking connection” field should be populated with your wireguard interface name (picture shows Wireguar_Server but should be wg_server if you are following the tutorial).
Technically you can do this through the windows gui using the properties menu manually, but having this script will allow you to automate the server start/stop process as you will see later on in the tutorial.
Now everything should be working correctly, the client should be able to reach the internet and LAN network you allow it to.
Step 3.1: Change default Internet Connection Sharing IP
By default, when internet sharing (NAT) is enabled, Windows will change the IP address of the adapter to something else (to avoid conflicts). However, we already know what ip address we want to adapter to be (set in the [interface] block in our wireguard config), which is 192.168.200.1 in our case.
To modify the default IP Windows will switch to, we can simply change the setting in registry.
Open Registry Editor and go to the following path:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\SharedAccess\Parameters
Then simply change ScopeAddress and StandaloneDHCPAddress to the IP address we desire (192.168.200.1 in our case).

Disable and re-enable Internet connection sharing (NAT) using the powershell command in Step 3 to make sure this change takes place (you might need to restart computer).
Step 4: Enable persistent Internet Sharing on restart (updated 2/12/2020)
Since there is a windows bug that internet connection sharing will not auto start on reboot, we need to change a few settings to make sure internet sharing is started. The earlier tutorial used a scheduled task to accomplish this, but I’ve found a better way after reading the windows bug fix here.
Open the Service window and find “Internet Connection Sharing”:

Chang the startup type to “Automatic”:

After that’s done, finally we add a registry:
Path: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\SharedAccess
Type: DWORD (32bit)
Setting: EnableRebootPersistConnection
Value: 1

Step 4.1 (optional) Bat files to easily start / stop server manually
For convenience I also made two bat files to run these commands so I don’t have to open comman prompt or powershell everytime to start and stop the server.
Server start batch script: (save as “start.bat” and run with admin privilege)
@echo off
"C:\Program Files\WireGuard\wireguard.exe" /installtunnelservice "C:\Henry-Scripts\Wireguard_Server.conf"
"%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" Set-NetConnectionSharing "Wireguard_Server" $true
Server stop batch script: (save as “stop.bat” and run with admin privilege)
@echo off
"%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" Set-NetConnectionSharing "Wireguard_Server" $false
"C:\Program Files\WireGuard\wireguard.exe" /uninstalltunnelservice Wireguard_Server
Final Remarks:
Compared to Linux, setting up a windows wireguard server can be tricky.
However, I have done most of the ground work for you (the powershell script to enable NAT).
Running the powershell script on startup with 3 minutes delay is not elegant, but it works.
There should be a way to run the task after the wireguard service is started and running, but I wasn’t able to get it to work. If you know how to get it to work, please share it with me.
If you like my work feel free to buy me a coffee:
does it ok with windows server? I make it in win server, but it said ics confict with “routing and remote access service” then I remove ras ,the ics still could not run (start in services.msc).
Should “StandaloneDHCPAddress” in paragraph Step 3.1: Change default Internet Connection Sharing IP …
be instead “ScopeAddressBackup” like it’s shown in the image below?
Inspired by this paper I have tried to simplify this task even further. Just put this piece of software to public after testing it internally. Hope it will be helpful: https://www.wiresock.net
I really cannot get this to work with NAT. I even tried manually share Internet, and Bridge, but nothing works. The commmand line gives me errors mentioned earlier in the post section. the VB script doesn’t work either.
Everything worked up to the NAT.
VB script worked for me instead of PS, code below:
Private Sub EnableDisableICS(ByVal sPublicConnectionName, ByVal sPrivateConnectionName, ByVal bEnable)
Dim bFound
Dim oNetSharingManager, oConnectionCollection, oItem, EveryConnection, objNCProps
Set oNetSharingManager = Wscript.CreateObject(“HNetCfg.HNetShare.1”)
Set oConnectionCollection = oNetSharingManager.EnumEveryConnection
For Each oItem In oConnectionCollection
set EveryConnection = oNetSharingManager.INetSharingConfigurationForINetConnection(oItem)
set objNCProps = oNetSharingManager.NetConnectionProps(oItem)
If objNCProps.name = sPrivateConnectionName Then
bFound = True
‘MsgBox(“Starting Internet Sharing For: ” & objNCProps.name)
If bEnable Then
EveryConnection.EnableSharing(1)
Else
EveryConnection.DisableSharing()
End If
End If
Next
Set oConnectionCollection = oNetSharingManager.EnumEveryConnection
For Each oItem In oConnectionCollection
Set EveryConnection = oNetSharingManager.INetSharingConfigurationForINetConnection(oItem)
Set objNCProps = oNetSharingManager.NetConnectionProps(oItem)
If objNCProps.name = sPublicConnectionName Then
bFound = True
‘MsgBox(“Internet Sharing Success For: ” & objNCProps.name)
If bEnable Then
EveryConnection.EnableSharing(0)
Else
EveryConnection.DisableSharing()
End If
End If
Next
End Sub
‘ Call SUB
EnableDisableICS “Local Area Connection”, “wg_server”, True
I did the Internet Connection Sharing through the GUI, without using the script you provided. We need to make it on the Local connection, not the Wireguard one. So, we are sharing the Local area connection and later in the settings “Home networking connection” should be the Wireguard connection.
For the settings to survive a restart, we need to do the registry key as you described (EnableRebootPersistConnection) and set the service “Internet Connection Sharing (ICS)” to start Automatic (Delayed). The (Delayed) part is critical, so it starts AFTER the Wireguard tunnel. This way the script is not needed.
Also I did not change the automatic IP Windows gives to the Wireguard interface, it remained 192.168.137.1, although the Wireguard IP is different.
I’m using Windows 10 Pro 20H2 at the time of writing.
Thank you again for the wonderful walkthrough. It made the logic in Windows more clear to me.
Designed with simplicity in mind… Yeah right!!!
I can’t seem to get server routing to work, the script runs without error on ps but the wireguard ethernet wont show up in the sharing tab, any idea why?
For Windows 10, the following helps to configure NAT:
Set-ExecutionPolicy -ExecutionPolicy Bypass
Import-Module wireguard
For windows Server this would make windows function more like an forward all router:
If ((Get-WindowsFeature -Name Routing).InstallState -ne ‘Installed’) {
Install-WindowsFeature -Name Routing -Confirm:$false -IncludeManagementTools
}
If ((Get-RemoteAccess).RoutingStatus -ne ‘Installed’) {
Install-RemoteAccess -VpnType RoutingOnly
}
If ((Get-Service -Name RemoteAccess).Status -ne ‘Running’) {
Start-Service -Name RemoteAccess -Confirm:$false
}
Good day, excellent article!
I have a Win10 machine that I plan to use as a wireguard server. This machine has a main internet network adapter + OpenVPN client connection that is used for selected routes.
If I understand correctly described above wireguard VPN setup will only allow my wireguard clients to access main internet interface but not the OpenVPN connection, please correct me I’m wrong.
Is there a way for a wireguard client to use all available connections and honor existing routes configuration on wireguard server?
Thank you!
I have two ideas that may work:
1) Bridge the network through the local router’s routing table.
This one is easier as you can just add a static route and bridge the two networks that way.
2) Add an artificial adapter in windows to bridge the two adapter.
I don’t know whether this will work or not, but it is worth a try if you don’t want to rely on the router.
Interesting option here for a low cost vpn option. We use a much fatter client for vpn and am exploring WG as an alternative option. My only reasoning for our users to use the (fat) vpn client is to sync their usernames with our internal AD servers keeping their laptops, AD, and O365 password in sync. Would installing a WG server resolve this issue whereby the ‘remote’ users could activate (or have running the whole time) the client to connect to our box – which is on the same network subnet as our AD boxes – and then be able to change their passwords? I presume that if the WG server is on the same subnet mask as the AD boxes that they could talk on whatever protocol they needed w/o any additional routing needed. Would this be correct? Would it be correct to also say that having a WG client connected that their ‘tunnel ‘would be a ‘split tunnel’ process? By split tunnel i mean that only traffic routed to the internal WG network would go that way and all other traffic would route to the internet?
Thanks, I’ll try again and tell him
Good afternoon, in principle I have done everything manually and it works, I will copy the scripts again in case I copy them wrong. greetings and thank you very much
Good afternoon, sorry again for the inconvenience.
I have connected a client computer with windows 10 and a mobile phone, I ping the 192.168.200.1 and from the server to the clients, but I can’t get them to surf the internet or reach the lan network. operations performed. Ipenablerouter enabled just in case,
Server ethernet interface shared with server wireguard interface.
Primary DNS suffix. . . . . :
Node type. . . . . . . . . . : hybrid
IP routing enabled. . . : yes
WINS proxy enabled. . . . . : no
Unknown adapter server:
Specific DNS suffix for the connection. . :
Description. . . . . . . . . . . . . . . : Wintun Userspace Tunnel
Physical address. . . . . . . . . . . . . :
DHCP enabled. . . . . . . . . . . . . : no
Automatic configuration enabled. . . : yes
IPv4 address. . . . . . . . . . . . . . : 192.168.200.1 (Preferred)
Subnet mask . . . . . . . . . . . . : 255.255.255.0
It seems like you still have trouble with enabling network sharing.
Make sure you use the right term in => Set-NetConnectionSharing “wg_server” $true
If you use an adapter name other than “wg_server” you should replace it accordingly.
From what I’ve seen you seem to have an adaptor named “Unknown adapter server”
Try to rename that to wg_server
Good morning, after performing the following configurations on the wireguard server computer in windows 10, whose interfaces are: ethernet 192.168.10.2/24 and the wireguard interface is wg_server with ip 172.16.0.1. On the other hand, the ip of the wireguard client in windows 10 is 172.16.0.2, and the lan of the client 192.168.20.0/24 I ping between these wireguard interfaces, that is, from the windows 10 client whose ip is 172.16.0.2/24 I ping to the wireguard interface of the server 172.16.0.1/24 but I do not ping the lan of the server nor do I have internet access. I think it is routing problems. In the server from network connections, I have gone to the ethernet interface and I have activated sharing and I have selected the wg_server interface. restarted all services, opening port 51820 in both udp computers, in the router also for the nat.
On the other hand I have tried to add the routes with route add 192.168.10.0 mask 255.255.255.0 172.16.0.1 but it did not lead to the server, how could I solve it. I have followed all the steps and scrits again but it gives me an error as I comment
PS C:\WINDOWS\system32> Import-Module wireguard
PS C:\WINDOWS\system32> Set-NetConnectionSharing “wg_server” $true
Excepción al llamar a “Invoke” con los argumentos “1”: “No se puede procesar el argumento porque el valor del
argumento “arguments” es NULL. Cambie el valor del argumento “arguments” a un valor no nulo.”
En C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\wireguard\wireguard.psm1: 49 Carácter: 9
+ $publicConfig = $netShare.INetSharingConfigurationForINetConn …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : PSArgumentNullException
No se puede llamar a un método en una expresión con valor NULL.
En C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\wireguard\wireguard.psm1: 54 Carácter: 13
+ $publicConfig.EnableSharing(0)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Un evento no pudo invocar a ninguno de los subscriptores (Excepción de HRESULT: 0x80040201)
En C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\wireguard\wireguard.psm1: 55 Carácter: 13
+ $privateConfig.EnableSharing(1)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException