wireguard VPN server

Wireguard is a tool for creating VPNs, it’s modern, secure and performant. Let’s see how to create a VPN server and send all our traffic through it.


We’ll need a server with at lease these specs:

  • 1 core
  • 512mb of RAM (it works even with less than that, but let’s try to be reasonable)
  • 5GB of storage
  • bandwidth!
  • linux with a recent kernel

Pay attention to the available bandwidth: if you’re so lucky to have a fast internet connection, let’s say >100 mbit, we need to have at least the same bandwidth on the server.

As of today, we can rent a VPN with these specs for less than 5€/month.

In this howto, we’ll consider these operating systems:

  • Ubuntu Server 20.04
  • Debian 10 Buster


We’ll use these conventions:

  • Tunnel network:
  • Server IP:
  • Clients IP: from
  • Server external IP:
  • Server external interface: eth0

server setup

For simplicity, suppose we start with a fresh server, to which we have access as root user.

If we’re using Debian Buster, we have to enable backports:

1# echo "deb buster-backports main" \
2| tee /etc/apt/sources.list.d/debian-backports.list && apt update

Also for Debian Buster, we can optionally install a more recent kernel:

1# apt install linux-image-5.7.0-0.bpo.2-amd64

Now we can install some packages:

1# apt install wireguard iptables resolvconf qrencode unattended-upgrades unbound

Wireguard needs a public and a private key for each connection member, so let’s create a key pair for the server:

1# wg genkey | tee /root/server.privkey
4# cat /root/server.privkey | wg pubkey | tee /root/server.pubkey

To simplify the configuration, we’ll use a wireguard tool called wg-quick: this allows the management of each wireguard interface in a file; moreover, it’ll manage IP addresses and routing at each interface ativation/deactivation. The public keys and IP addresses of every other VPN member will be saved in this configuration file. Let’s start by creating the configuration file (the file name MUST be [interface_name].conf) and populate it:

 1# touch /etc/wireguard/wg0.conf
 3# cat /etc/wireguard/wg0.conf
 5Address =
 6SaveConfig = true
 7ListenPort = 51194
 8PrivateKey = sL1UKqiqMraaQarCy7UIUpkQnpgzR6Gm+L1RCgp2TEM=
10# systemctl enable wg-quick@wg0
12# wg-quick up wg0
13[#] ip link add wg0 type wireguard
14[#] wg setconf wg0 /dev/fd/63
15[#] ip -4 address add dev wg0
16[#] ip link set mtu 1420 up dev wg0
18# wg
19interface: wg0
20  public key: UgvmsCnMWWW9XAHNbc6+lkbCLSF5Mt3b85A4PrG4mRE=
21  private key: (hidden)
22  listening port: 51194

Let’s put the server aside for a moment and check the client.

client setup (linux)

We have to replicate on the client the same commands for the server, with some little differences: clearly the key pair will be different, we’re also missing IP and port for connection receive because the connection will only be outgoing.

Suppose we’re using an Ubuntu 20.04:

 1# sudo apt install wireguard resolvconf
 3# wg genkey | tee ~/server.privkey
 6# cat ~/server.privkey | wg pubkey | tee ~/server.pubkey
 9# sudo touch /etc/wireguard/wg0.conf
11# sudo tee /etc/wireguard/wg0.conf <<EOF
13SaveConfig = true
14PrivateKey = AGrgj2nh5T/3VMddrno/FIOgiotgKVQ9ydjw2AHzbno=
15Address =
16DNS =
19PublicKey = phoJ2IBLJXEjaJJXzcEM6TGidh/rGxCdpXvKOP0HK0E=
20Endpoint =
21AllowedIPs =
22# AllowedIPs =
23PersistentKeepalive = 20
26# sudo systemctl enable wg-quick@wg0
28# sudo wg-quick up wg0
29[#] ip link add wg0 type wireguard
30[#] wg setconf wg0 /dev/fd/63
31[#] ip -4 address add dev wg0
32[#] ip link set mtu 1420 up dev wg0
33[#] ip -4 route add dev wg0

in summary, the client has a single IP address (/32) and a DNS. In the [Peer] section, we’re pointing at a specific server IP and port (but you can also use a FQDN), as well as the public key to certify its identity and decrypt traffic.

The AllowedIPs line indicates which IP addresses are forwarded over the tunnel, in this case all. Consequently, when the VPN is active, it will be impossible to see the local network (and any other network not reachable by the VPN server) and all traffic will pass through the VPN.

client configuration on the server

The server must be instructed to accept client connections:

 1# wg set wg0 peer 7c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M= \
 4# wg
 5interface: wg0
 6  public key: UgvmsCnMWWW9XAHNbc6+lkbCLSF5Mt3b85A4PrG4mRE=
 7  private key: (hidden)
 8  listening port: 51194
 9  fwmark: 0xca6c
11peer: 7c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M=
12  endpoint:
13  allowed ips:
14  latest handshake: 39 seconds ago
15  transfer: 7.20 MiB received, 6.63 MiB sent
17# wg-quick save wg0
18[#] wg showconf wg0

The last command is used to immediately commit the configuration into /etc/wireguard/wg0.conf.

We still need a couple of tweaks: masquerading and DNS.

Masquerading (and forwarding)

At the moment, the traffic from wireguard VPN arrives on the server and is discarded. Forwarding must be enabled:

1# echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-forwarding.conf
3# sysctl -w net.ipv4.ip_forward=1

Let’s also create some firewall rules for traffic management:

1# iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
2# iptables -A FORWARD -i wg0 -j ACCEPT
3# iptables -P FORWARD DROP
4# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Which means:

  • the traffic to be forwarded, if related to existing connections, is accepted
  • the traffic to be forwarded, coming from the VPN is accepted
  • every other traffic to be forwarded, if not managed by other rules, will be dropped
  • traffic coming out from eth0 server port is masqueraded

Let’s make it permanent:

1# iptables-save > /etc/iptables.up.rules
3# cat > /etc/network/if-pre-up.d/iptables <<EOF
5/sbin/iptables-restore < /etc/iptables.up.rules
8# chmod +x /etc/network/if-pre-up.d/iptables


Client uses the DNS service on the server, so we need to configure it:

 1# cat > /etc/unbound/unbound.conf.d/custom.conf <<EOF
 3access-control: allow
 4access-control: allow
 7        name: "."
 8        forward-addr:
 9        forward-addr:
12# service unbound restart

We simply enable the DNS service on ALL interface, but we’re accepting requests only from VPN and localhost; every request is forwarded to quad9 and cloudflare DNS.


  • malware/ad filtering
  • Windows and OSX client instructions?