The Lab

Adding My iPhone to WireGuard

How I added an iPhone as a separate WireGuard peer without reusing my laptop config.

I already had WireGuard running on the server. My laptop was the only client, which was fine until I wanted to check something from my phone.

The iPhone had the WireGuard app installed already, so this was mostly a peer management job: give the phone its own identity, add it to the server, import the config, then check for a handshake.

The important part is not copying the laptop config.

iPhone sitting on a desk beside a laptop

The phone needs its own WireGuard identity, not a copy of the laptop identity. Photo: www.Pixel.la Free Stock Photos, CC0, via Wikimedia Commons.

The peer rule

Every WireGuard device should be its own peer.

The laptop already had a private key, public key, and VPN address. If I reused that same config on the iPhone, the server would see both devices as the same peer. Whichever device spoke most recently would become the current endpoint for that peer.

That gets messy quickly. One device appears to work, the other drops off, and the tunnel starts looking unreliable even though the real issue is duplicate peer identity.

So the iPhone gets:

  1. Its own private key
  2. Its own public key
  3. Its own VPN IP address
  4. Its own [Peer] block on the server

My WireGuard subnet is 10.0.0.0/24. The laptop was already using 10.0.0.2/32, so the iPhone got 10.0.0.3/32.

Check the current tunnel

First, check the existing peers:

sudo wg show
sudo wg show wg0 allowed-ips

The useful part is the allowed ips list. Existing peers look like this, with real public keys in place of the placeholders:

<laptop-public-key>    10.0.0.2/32

The iPhone needs a different address:

<iphone-public-key>    10.0.0.3/32

Those are private VPN addresses. They are not the server’s public internet address.

Generate iPhone keys

On the server, create a client directory and generate a key pair for the phone:

sudo mkdir -p /etc/wireguard/clients
sudo chmod 700 /etc/wireguard/clients

sudo sh -c 'umask 077; wg genkey > /etc/wireguard/clients/iphone.key'
sudo sh -c 'wg pubkey < /etc/wireguard/clients/iphone.key > /etc/wireguard/clients/iphone.pub'

The private key stays on the server long enough to build the phone config, then it is imported into the WireGuard app.

The public key is what the server uses to recognise the iPhone:

sudo cat /etc/wireguard/clients/iphone.pub

Add the phone to the server

Append a new peer block to /etc/wireguard/wg0.conf:

IPHONE_PUB="$(sudo cat /etc/wireguard/clients/iphone.pub)"

sudo tee -a /etc/wireguard/wg0.conf >/dev/null <<EOF

[Peer]
# iPhone
PublicKey = $IPHONE_PUB
AllowedIPs = 10.0.0.3/32
EOF

Then apply the peer to the running tunnel without dropping the current connection:

sudo wg set wg0 peer "$IPHONE_PUB" allowed-ips 10.0.0.3/32

Check it:

sudo wg show wg0 allowed-ips

At this point the server should know about the laptop and the iPhone as separate peers.

Build the iPhone config

The iPhone needs a client config. This is the file the WireGuard app imports.

The commands below read the key values from the server, so there is no need to copy them by hand. The only value to set manually is the public endpoint that reaches the WireGuard UDP port.

Get the server’s public IP if you do not already use a DNS name:

curl -4 ifconfig.me

Then set the endpoint:

WG_ENDPOINT="<server-public-ip-or-dns-name>:51820"

WireGuard uses UDP. A normal HTTP tunnel hostname is not automatically a WireGuard endpoint. Use the public IP or DNS name that actually reaches UDP port 51820 on the server.

Create the iPhone config:

SERVER_PUBLIC_KEY="$(sudo wg show wg0 public-key)"
IPHONE_PRIVATE_KEY="$(sudo cat /etc/wireguard/clients/iphone.key)"

sudo sh -c 'umask 077; cat > /etc/wireguard/clients/iphone.conf' <<EOF
[Interface]
PrivateKey = ${IPHONE_PRIVATE_KEY}
Address = 10.0.0.3/32
DNS = 1.1.1.1

[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
Endpoint = ${WG_ENDPOINT}
AllowedIPs = 10.0.0.0/24
PersistentKeepalive = 25
EOF

That AllowedIPs line is intentional:

AllowedIPs = 10.0.0.0/24

It means the iPhone only sends traffic for the VPN subnet through WireGuard. That was enough for server access.

If the goal is to route all iPhone internet traffic through the server, the line changes to:

AllowedIPs = 0.0.0.0/0, ::/0

I would not start there for a simple server-access setup. Full-tunnel VPN needs the server’s forwarding, NAT, firewall, and DNS setup to be right. For this use case, the smaller route is cleaner.

Import it with a QR code

Install the QR tool:

sudo apt update
sudo apt install -y qrencode

Generate the QR in the terminal:

sudo sh -c 'qrencode -t ansiutf8 < /etc/wireguard/clients/iphone.conf'

The sudo sh -c wrapper is important.

This version can fail:

sudo qrencode -t ansiutf8 < /etc/wireguard/clients/iphone.conf

The problem is the < /etc/wireguard/clients/iphone.conf part. The shell opens the file before sudo runs, so a protected config file can throw Permission denied.

Small shell detail, easy to miss.

On the iPhone:

  1. Open WireGuard
  2. Tap +
  3. Choose Create from QR code
  4. Scan the terminal QR
  5. Approve the VPN profile
  6. Turn the tunnel on

Verify the handshake

Back on the server:

sudo wg show

The iPhone peer should show a recent handshake:

peer: <iphone-public-key>
  endpoint: <iphone-current-network>:<port>
  allowed ips: 10.0.0.3/32
  latest handshake: <recent time>
  transfer: <traffic counters>

No handshake usually means one of these needs checking:

  • The endpoint is not reaching the server
  • UDP port 51820 is not forwarded or allowed
  • The server public key in the phone config is wrong
  • The iPhone peer public key in wg0.conf is wrong
  • The iPhone is using an IP already assigned to another peer

Test access

Find the server’s WireGuard address:

ip -4 addr show wg0

In this setup the server is 10.0.0.1, so SSH over the VPN looks like:

ssh <user>@10.0.0.1

For a web service bound to the server, test the VPN address in Safari:

http://10.0.0.1

If the tunnel handshakes but the service does not load, WireGuard is probably working. At that point I would check the service binding, firewall rules, and whether the app is only listening on 127.0.0.1.

Network switches and Ethernet cabling

Once the tunnel is up, the phone behaves like another device on the private VPN network. Photo: Jon “ShakataGaNai” Davis, CC BY-SA 3.0, via Wikimedia Commons.

The result

The laptop remains one peer. The iPhone is a second peer. Both can connect without overwriting each other’s endpoint.

That is the main thing to remember with WireGuard: peers are identities, and every device gets its own.