WireGuard Server: Google Cloud Platform
In this tutorial, we setup a WireGuard service in Google Cloud Platform. Open a Google Cloud Platform account, which is free.
At the end of this tutorial, a new Compute Engine VM will have a virtual network interface
living on private network
The Compute Engine VM will be ready to add WireGuard clients.
In this section, we’ll use the GCP web console.
Create a new project just for WireGuard.
In this example, the project name is
For subsequent steps, you’ll need to decide which region your WireGuard service will live in.
In this example,
us-west1 (Oregon) is chosen because it happens to be geographically close.
Open a Port
The new project comes pre-configured with a VPC network named
and firewall rules that block inbound packets to all but a few ports.
WireGuard uses UDP, and is commonly configured to listen on port 51820.
Navigate to VPC network > Firewall rules.
Click “Create Firewall Rule” near the top of the page.
- Give the rule a name. Here, I’m using
- This is an ingress rule on the default network.
- Targets include “All instances in the network”.
- Source filter is “IP ranges”, specifically
- Under “Protocols and ports”, select “Specified protocols and ports”.
- Check “udp” and enter 51820.
- Click “Create”.
Create a Static IP Address
We’ll need to reserve a static IP address so that our clients know where to find the service.
Navigate to VPC network > External IP addresses.
Click “Reserve Static Address” in the dialog (or near the top of the page if you already have an address or two here).
- Give the IP address a name. Here, I’m using
- Use the premium network service tier, but be aware that two network tiers exist and this might matter depending on your reason for using a VPN.
- We’ll use IPv4 and “regional” type.
- Select the region where you intend to run the WireGuard service.
- Click “Reserve”.
Create a VM Instance
The VM instance is the virtual machine where our WireGuard service will run.
Navigate to Compute Engine > VM instances.
Click “Create” in the dialog (or click “Create Instance” near the top of the page if you already have an instance or two here).
- Give the VM instance a name. Here, I’m using
- Select the region where you intend to run the WireGuard service. The zone selection doesn’t matter.
- The machine type drives the price you’ll pay each month. In this example, I’ve selected family “General-purpose”, Series “N1”, Machine type “f1-micro” because the first 744 hours are free every month, and because the performance is good enough.
- Boot disk is how we select an operating system. In this example, I’ve selected “Debian GNU/Linux 10 (buster)”.
- Assign the reserved static IP address.
- Click “Management, security, disks, networking, sole tenancy”.
- Click “Networking”.
- Click the “default” network interface.
- Under “External IP” select the static IP address created in the previous section.
- Click “Done” in the network interface section
- Click “Create” at the bottom of the page.
Watch for the green check mark indicating that the new VM instance has initialized.
In this section, we’ll use the shell on our VM instance to install and configure WireGuard.
Open a terminal on the new VM instance.
Under the “Connect” column, click “SSH”, or use the
gcloud command via your local machine’s console.
To install the most recent version of WireGuard, we’ll need packages from the Debian unstable release. Add the Debian unstable release, and pin the Debian unstable priority behind Raspbian stable. This allows us to install packages that are not available in Debian stable, while keeping the “stable” versions of everything else.
$ sudo sh -c "echo 'deb http://deb.debian.org/debian/ unstable main' >> /etc/apt/sources.list.d/unstable.list" $ sudo sh -c "printf 'Package: *\nPin: release a=unstable\nPin-Priority: 90\n' >> /etc/apt/preferences.d/limit-unstable"
Update package information from both stable and unstable package repositories.
$ sudo apt update Get:1 http://deb.debian.org/debian unstable InRelease [142 kB] Hit:2 http://archive.raspberrypi.org/debian buster InRelease Hit:3 http://raspbian.raspberrypi.org/raspbian buster InRelease Get:4 http://deb.debian.org/debian unstable/main armhf Packages [7,977 kB] Get:5 http://deb.debian.org/debian unstable/main Translation-en [6,192 kB] Fetched 14.3 MB in 22s (655 kB/s) Reading package lists... Done Building dependency tree Reading state information... Done All packages are up to date.
Install the WireGuard packages.
After this step,
man wg and
man wg-quick will work and the
wg command gets bash completion.
$ sudo apt install wireguard --assume-yes Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: dkms raspberrypi-kernel-headers wireguard-dkms wireguard-tools Suggested packages: python3-apport menu The following NEW packages will be installed: dkms raspberrypi-kernel-headers wireguard wireguard-dkms wireguard-tools 0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded. ... DKMS: install completed. Module build for kernel 4.19.75-v8+ was skipped since the kernel headers for this kernel does not seem to be installed. Setting up wireguard-tools (0.0.20191219-1) ... Setting up wireguard (0.0.20191219-1) ... Processing triggers for man-db (2.8.5-2) ...
Also install linux-headers-VERSION, as it seems the Debian WireGuard packages don’t declare that dependency, or it’s just broken in GCP.
$ sudo apt install linux-headers-$(uname -r) Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: ... Fetched 556 kB in 0s (7381 kB/s) Selecting previously unselected package linux-headers-4.19.0-6-cloud-amd64. (Reading database ... 60668 files and directories currently installed.) Preparing to unpack .../linux-headers-4.19.0-6-cloud-amd64_4.19.67-2+deb10u2_amd64.deb ... Unpacking linux-headers-4.19.0-6-cloud-amd64 (4.19.67-2+deb10u2) ... Setting up linux-headers-4.19.0-6-cloud-amd64 (4.19.67-2+deb10u2) ...
In every client/server relationship, each peer has it’s own private and public keys. Create private and public keys for the WireGuard service. Protect the private key with a file mode creation mask.
$ (umask 077 && wg genkey > wg-private.key) $ wg pubkey < wg-private.key > wg-public.key
Print the private key, we’ll need it soon.
$ cat wg-private.key qPF9uU7qsCbw3uKR1t2Q0gfr2HasTKZGPkCHz2AszUs=
Create the WireGuard Network Device
Create the WireGuard service config file at
(Use a command like
sudo nano /etc/wireguard/wg0.conf.)
# define the WireGuard service [Interface] # contents of file wg-private.key that was recently created PrivateKey = qPF9uU7qsCbw3uKR1t2Q0gfr2HasTKZGPkCHz2AszUs= # UDP service port; 51820 is a common choice for WireGuard ListenPort = 51820
Create the WireGuard network device at
(Use a command like
sudo nano /etc/network/interfaces.d/wg0.)
# indicate that wg0 should be created when the system boots, and on ifup -a auto wg0 # describe wg0 as an IPv4 interface with static address iface wg0 inet static # static IP address address 10.0.2.1/24 # before ifup, create the device with this ip link command pre-up ip link add $IFACE type wireguard # before ifup, set the WireGuard config from earlier pre-up wg setconf $IFACE /etc/wireguard/$IFACE.conf # after ifdown, destroy the wg0 interface post-down ip link del $IFACE
$ sudo ifup wg0
At any time, verify that the WireGuard configuration for
wg0 is what you expect:
$ sudo wg show wg0 interface: wg0 public key: 2efuG9OYmMPQpbkJ8CVxGlvQflY6p1u+o4wjcgGII0A= private key: (hidden) listening port: 51820
At any time, verify that the
wg0 network interface exists.
$ ip address show dev wg0 7: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 link/none inet 10.0.2.1/24 brd 10.0.2.255 scope global wg0 valid_lft forever preferred_lft forever