Graham Stevens
⟵ Home

WireGuard Setup Guide for iOS

16th January 2019 - I have updated the article to include DNS lookups over VPN too, where as previously they would have been going out via your ISP/WiFi/mobile provider.

WireGuard is the new kid on the block when it comes to VPNs. I could try and explain what it is and why you should switch from OpenVPN or IPsec to it, but they do a great job of that themselves:

WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPSec, while avoiding the massive headache.

Great” I hear you say, “but IPSec and OpenVPN are a pain to setup, let alone setup securely”. Fear not, WireGuard has you covered:

WireGuard aims to be as easy to configure and deploy as SSH. A VPN connection is made simply by exchanging very simple public keys – exactly like exchanging SSH keys – and all the rest is transparently handled by WireGuard.

This guide will walk you through how to setup WireGuard in a way that all your client outgoing traffic will be routed via another machine (server). This is ideal for situations where you don’t trust the local network (public or coffee shop wifi) and wish to encrypt all your traffic to a server you trust, before routing it to the Internet.

Server Setup Guide

I am making some assumptions here, such as using Ubuntu for your server OS, so please tweak to fit your situation.

First up, we need to make sure you’ve got the kernel headers installed, as well as enabling IPv4 forwarding:

sudo apt install linux-headers-$(uname -r)
sysctl net.ipv4.ip_forward=1
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-sysctl.conf  # Make the change persistent

Because WireGuard isn’t currently part of the distributed Ubuntu packages, we’ll add the helpful PPA repository to keep things easy and up to date.

sudo add-apt-repository ppa:wireguard/wireguard
sudo apt update
sudo apt install wireguard
sudo modprobe wireguard

With WireGuard now installed, we need to generate a public and a private key – fortunately, this is a simple one-liner:

wg genkey | tee /etc/wireguard/server-privatekey | wg pubkey > /etc/wireguard/server-publickey

Next up, we need to create a WireGuard config file:

sudo nano /etc/wireguard/wg0.conf

Address =
Address = fd86:ea04:1115::1/64
SaveConfig = true
# Change eth0 to your network interface if it differs
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A INPUT -s -p tcp -m tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT; iptables -A INPUT -s -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server-privatekey>

We also need a DNS server running, to forward our requests from the clients rather than letting the clients go out to their local/ISP provided DNS. I have pinched most of this from ck’s Wireguard setup guide.

apt-get install unbound unbound-host
curl -o /var/lib/unbound/root.hints
sudo nano /etc/unbound/unbound.conf

  num-threads: 4
  #Enable logs
  verbosity: 1
  #list of Root DNS Server
  root-hints: "/var/lib/unbound/root.hints"
  #Use the root servers key for DNSSEC
  auto-trust-anchor-file: "/var/lib/unbound/root.key"
  #Respond to DNS requests on all interfaces
  max-udp-size: 3072
  #Authorized IPs to access the DNS Server
  access-control:                 refuse
  access-control:                 allow
  access-control:         allow
  #not allowed to be returned for public internet  names
  # Hide DNS Server info
  hide-identity: yes
  hide-version: yes
  #Limit DNS Fraud and use DNSSEC
  harden-glue: yes
  harden-dnssec-stripped: yes
  harden-referral-path: yes
  #Add an unwanted reply threshold to clean the cache and avoid when possible a DNS Poisoning
  unwanted-reply-threshold: 10000000
  #Have the validator print validation failures to the log.
  val-log-level: 1
  #Minimum lifetime of cache entries in seconds
  cache-min-ttl: 1800 
  #Maximum lifetime of cached entries
  cache-max-ttl: 14400
  prefetch: yes
  prefetch-key: yes

Last but not least, we need to tidy up some permissions and give unbound a kick to bring it to life.

sudo chown -R unbound:unbound /var/lib/unbound
sudo systemctl enable unbound
sudo systemctl start unbound

At this point, WireGuard on the server is complete and we could start it up if we wanted, but first, lets configure our client.

iOS Client Setup Guide

Our first client is an Apple iOS device. Currently WireGuard isn’t built into the operating system, unlike IPSec or IKEv2. However, we can easily overcome this hurdle thanks to the WireGuard iOS App which is currently in alpha and can be installed easily via TestFlight. Alternatively you can check out another 3rd party client produced by TunSafe which again can be installed via TestFlight.

With one of the two apps installed, lets generate a config server-side and we can transfer it later with a simple QR code. We could manually enter a config within the app, however moving around those keys can be a nightmare…

First up, client private and public keys:

cd /etc/wireguard
wg genkey | tee mobile-privatekey | wg pubkey > mobile-publickey

We need to configure our mobile client as a peer within wg0.conf, so that we can connect to our server.


# Our mobile client details
PublicKey = <contents of mobile-publickey>
AllowedIPs =, fd86:ea04:1115::2/128

That is everything we need to do for our server config - simple right?

To split things up a little, lets start WireGuard. We can bring the interface up with wg-quick up wg0, as well as down with wg-quick down wg0.

However if you would rather run it as a service so that it is always brought up on start-up or after reboots, we can utilise systemctl:

systemctl enable wg-quick@wg0  # Enable to start at boot-up
systemctl start wg-quick@wg0  # Start to.. well, start.

You can get the current status of WireGuard by simply running wg:

root@lon-vpn:/etc/wireguard# wg
interface: wg0
  public key: 3YjKyxxxxxxxxxxxxxxxxxxxxxxxxxxxsx856vfcQc=
  private key: (hidden)
  listening port: 51820

peer: x8roC6GOxxxxxxxxxxxxxxxxxxxxxxxdi7Uhol6xY=
  allowed ips:, fd86:ea04:1115::2/128

With WireGuard successfully running, lets create a conf file for our iOS device which we will transfer via QR code. We can do all of this whilst we are still on our server.

cd /etc/wireguard
nano mobile.conf

PrivateKey = <contents of mobile-privatekey>
Address =, fd86:ea04:1115::2/128

PublicKey = <contents of server-publickey>
Endpoint = <server-ip>:51820
AllowedIPs =, ::/0

Mobile config done. Lets get it transferred to our device. We will do this using a QR code, which we can create on our server using qrencode. You can install this via apt install qrencode.

To generate the QR code and display it on the screen:

qrencode -t ansiutf8 < /etc/wireguard/mobile.conf

A QR code generated from `mobile.conf`

Simply scan this code using one of the iOS apps mentioned previously, and you will be all setup! Enjoy routing all of your traffic via your server. You can verify this by visiting or similar on your device.

$ whoami I am Graham Stevens, a Cyber Security Specialist based in the South West of the UK. If you're lucky, I very occasionally post updates on Twitter/Mastodon.