ProjectsTutorials

How to Setup Wireguard VPN Server On Windows

wireguard server for windows tutorial

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.

wireguard server for windows tutorial

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”:

wireguard server for windows tutorial

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:

31 thoughts on “How to Setup Wireguard VPN Server On Windows

  1. Thanks a lot for this thorough guide. You solved me a lot of headache with that script and detailed explanation.

    However, is it not at all possible getting this to work with multiple peers? I can get two servers running using the service method, although that method sucks if you would like to have a lot of clients. Still, the script that enables the network sharing/routing only works on a single interface. Is this a technical restriction?

    1. You can have multiple peers connecting to one server, unless you meant you want to have multiple servers?
      To enable network sharing on multiple interfaces, you can use the bridge function. Make a bridge interface say “wg_bridge” and bridge the Wireguard interfaces (wg_server1, wg_server2… etc.) together, then run the script on the bridged “wg_bridge” interface.

  2. Great post! I was able to get it up and running with these instructions with a slight modification to not specify the DNS so that it uses the VPN server’s DNS. This works as expected for FQDNs (myserver.internal.mynetwork.com) by not host names (myserver). Do you know if there’s anyway to set the “DNS Suffix Search List” on the client?

  3. Just a fyi, the EnableRebootPersistConnection registry fix doesn’t work for everyone (me included). No idea whether MS broke it again after the implementation or just didn’t fix it completely to start.

    Spent many many hours scouring for fixes to this problem and best I came up with was to have a .bat file run in startup the calls a powershell script that will toggle sharing on and off. This still needs the user to click ok on a powershell popup because MS doesn’t allow this type of function to be run without administrator input.

    Was very tempted to just use a VM running linux on the windows machine but since computer running server is basically always on I’ve learned to live with it.

    Nice writeup, wish it was around in December when I set my machines/devices up. Had to cobble together random posts from all over but once understood enough it was a breeze compared to openvpn.

    1. You can use Task Schedule to run the bat files as administrator on startup to avoid the need to click the popup. This used to be part of the original tutorial before I found the hotfix.

  4. hi,

    where to get “PublicKey = #Replace with client public key#
    and
    PresharedKey = #Replace with pre-shared key#”

    best wishes!

    1. Creating presharedkey, public key, and private key is not the focus of this tutorial. It is part of the standard server creation process, which there are already plenty of tutorials out there. You can even use the Windows Wireguard GUI tool to create them.

  5. Thank you for this article.
    Can I setup a wireguard vpn between a windows client and a virtual Linux server (hosted by the windows client with VMWare Workstation) ?

  6. C:\windows\system32\WindowsPowerShell\v1.0\Modules\wireguard> Set-NetConnectionSharing
    The term ‘Set-NetConnectionSharing’ is not recognized as the name of a cmdlet, function, script file, or operable progr
    am. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At line:1 char:25
    + Set-NetConnectionSharing <<<<
    + CategoryInfo : ObjectNotFound: (Set-NetConnectionSharing:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

  7. PS C:\> Set-NetConnectionSharing “Wireguard_Server” $true
    Exception calling “Invoke” with “1” argument(s): “Cannot process argument because the value of argument “arguments” is
    null. Change the value of argument “arguments” to a non-null value.”
    At C:\windows\system32\windowspowershell\v1.0\Modules\wireguard\wireguard.psm1:50 char:9
    + $privateConfig = $netShare.INetSharingConfigurationForINetConnection.Inv …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : PSArgumentNullException

    You cannot call a method on a null-valued expression.
    At C:\windows\system32\windowspowershell\v1.0\Modules\wireguard\wireguard.psm1:55 char:13
    + $privateConfig.EnableSharing(1)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

  8. Good afternoon, after importing the wireguard module it generates the following error

    PS C: \ WINDOWS \ system32> Set-NetConnectionSharing “Windows10” $ true
    Exception when calling “Invoke” with arguments “1”: “The argument cannot be processed because the value of the
    argument “arguments” is NULL. Change the value of the “arguments” argument to a non-null value. ”
    In C: \ WINDOWS \ system32 \ WindowsPowerShell \ v1.0 \ Modules \ wireguard \ wireguard.psm 1:49 Character: 9
    + $ publicConfig = $ netShare.INetSharingConfigurationForINetConn …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~
    + CategoryInfo: NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId: PSArgumentNullException

    You cannot call a method on a NULL expression.
    In C: \ WINDOWS \ system32 \ WindowsPowerShell \ v1.0 \ Modules \ wireguard \ wireguard.psm 1:54 Character: 13
    + $ publicConfig.EnableSharing (0)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo: InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId: InvokeMethodOnNull

  9. Thank you, I will do all the settings again. Let’s see if I can get the wireguard server up and running on windows.

  10. 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

  11. 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

    1. 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

    1. 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

  12. 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?

  13. 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!

    1. 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.

  14. 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
    }

  15. For Windows 10, the following helps to configure NAT:

    Set-ExecutionPolicy -ExecutionPolicy Bypass
    Import-Module wireguard

Leave a Reply

Your email address will not be published. Required fields are marked *