· 7 min read

WireGuard VPN Server Setup on Linux: NAT, Clients, Mobile

A WireGuard VPN server setup on Linux takes about ten minutes: install the package, generate a key pair, define a wg0 interface, enable IP forwarding, and add a NAT rule so clients can reach the internet through the server. This guide walks through each step on Ubuntu/Debian, then covers desktop and mobile clients, split tunneling, and the mistakes that cause most "connected but no traffic" problems.

All addresses below use documentation ranges. The server's public IP is 203.0.113.10, and the tunnel network is 10.8.0.0/24. Replace both with your own values.

What You Need

Step 1: Install WireGuard

sudo apt update
sudo apt install -y wireguard qrencode

qrencode is optional, but it's the easiest way to onboard phones later.

Step 2: Generate the Server Keys

sudo -i
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
cat server.pub

The private key never leaves the server. The public key goes into each client config. The umask 077 keeps the key file readable only by root.

Step 3: Find Your Public Network Interface

NAT rules must reference the interface that faces the internet. Check its name:

ip -4 route show default

The output shows something like default via 203.0.113.1 dev eth0. Here the interface is eth0. On many systems it's ens3, ens192, or enp1s0. A wrong name here is the most common cause of a tunnel that connects but passes no traffic.

Step 4: Create the Server Config

Create /etc/wireguard/wg0.conf:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of server.key>

PostUp   = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

# Peers are added below

Lock down the file and keep the key out of shell history:

chmod 600 /etc/wireguard/wg0.conf

The PostUp and PostDown lines add and remove the forwarding and masquerade rules when the interface goes up or down, so nothing lingers after a stop.

If you use nftables natively, create the equivalent rule: a postrouting chain in a nat table with ip saddr 10.8.0.0/24 oifname "eth0" masquerade.

Step 5: Enable IP Forwarding

Without forwarding, the kernel drops packets that need to go from wg0 out through eth0.

echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

If you also want to route IPv6 through the tunnel, add net.ipv6.conf.all.forwarding=1 and give the tunnel a ULA or routed prefix. Don't hand out IPv6 addresses to clients unless the server can actually route them. A broken IPv6 path with ::/0 in the client's AllowedIPs causes slow, confusing failures.

Step 6: Open the Firewall and Start the Interface

With UFW:

sudo ufw allow 51820/udp
sudo ufw allow OpenSSH
sudo ufw enable

If UFW's default forward policy is DROP, the PostUp FORWARD rules above still apply, but check /etc/default/ufw if traffic stalls. Also open UDP 51820 in any provider-level or upstream firewall in front of the server.

Start it and enable it at boot:

sudo systemctl enable --now wg-quick@wg0
sudo wg show

Step 7: Create a Client Config

Generate keys for the client. Doing it on the server is fine for a small setup. For stricter hygiene, have each user generate their own key pair and send you only the public key.

cd /etc/wireguard
wg genkey | tee laptop.key | wg pubkey > laptop.pub
wg genpsk > laptop.psk

Add the peer to the server config:

[Peer]
# laptop
PublicKey = <contents of laptop.pub>
PresharedKey = <contents of laptop.psk>
AllowedIPs = 10.8.0.2/32

On the server, AllowedIPs means "which source addresses this peer may use." Give each client a single /32. Apply the change without dropping existing sessions:

sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'

The client config, laptop.conf:

[Interface]
PrivateKey = <contents of laptop.key>
Address = 10.8.0.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = <contents of server.pub>
PresharedKey = <contents of laptop.psk>
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

On the client, AllowedIPs means "which destinations go into the tunnel." Setting it to 0.0.0.0/0 sends all IPv4 traffic through the VPN (full tunnel). The DNS line points at a resolver you trust; you can also run your own resolver on the server and use 10.8.0.1.

Split Tunneling

If you only want to reach a private network or a few servers, list those ranges instead:

AllowedIPs = 10.8.0.0/24, 198.51.100.0/24

Only traffic to those ranges uses the tunnel, and everything else goes out normally. Remove the DNS line, or point it at an internal resolver, so you don't push all DNS lookups through the VPN unintentionally.

Linux, Windows, and macOS Clients

Mobile Clients (iOS and Android)

Create a config for the phone the same way, using a new key pair and the next free address, such as 10.8.0.3/32. Then render it as a QR code in the terminal:

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

In the official WireGuard app for iOS or Android, tap Add, choose "Create from QR code," and scan it. Treat the QR code like a password, because it contains the private key. Don't paste it into chat tools or screenshots.

For mobile, keep PersistentKeepalive = 25. Phones sit behind carrier NAT, and without keepalives the mapping expires and inbound packets stop arriving. Both apps also offer an on-demand option, which can bring the tunnel up automatically on cellular or untrusted Wi-Fi.

Verify It Works

On the server:

sudo wg show

A working peer shows a recent latest handshake and growing transfer counters. From the client, check that your public IP is now the server's:

curl -4 https://ifconfig.me

Common Mistakes and Fixes

Symptom Likely cause Fix
No handshake at all UDP 51820 blocked, or wrong Endpoint or key Run tcpdump -ni eth0 udp port 51820 on the server and check provider firewalls
Handshake works, no internet Forwarding off or wrong NAT interface Check sysctl net.ipv4.ip_forward and the -o eth0 name
Some sites hang, others load MTU too high Set MTU = 1380 in the client [Interface]
Phone drops after idle NAT mapping expired Set PersistentKeepalive = 25
Two clients fight each other Duplicate AllowedIPs or reused keys Use a unique /32 and key pair per device
DNS leaks DNS not set or not routed through the tunnel Set DNS = and use 0.0.0.0/0 for full tunnel

WireGuard's default MTU is 1420. That's right for most links, but PPPoE, LTE, and nested tunnels sometimes need a lower value.

Hardening Tips

Where to Run It

A WireGuard server needs a stable public IP, a reliable network path, and enough CPU for encryption. The load is small for a handful of users. A VDS is a good fit. Nixcon Group LLC runs its own network, AS40368, with locations in Izmir, Frankfurt, and Fremont, California. You can compare the paths from each location with the public looking glass before you choose where to deploy. Our VDS servers run on VMware vSphere, so you get a full Linux VM with a kernel that supports WireGuard.

If you'd rather not manage keys, firewalls, and updates yourself, see our VPN services.

Frequently asked questions

Is WireGuard faster than OpenVPN?

In most deployments WireGuard has lower overhead. It runs in the Linux kernel, uses a small modern cryptographic suite, and keeps the codebase compact. Real results depend on CPU, MTU, and the network path, so test on your own link.

Which port does WireGuard use?

There is no fixed port. UDP 51820 is the common default, and you can change ListenPort in wg0.conf. Open the same port in the server firewall and any upstream firewall.

Why does my WireGuard client connect but have no internet?

Usually IP forwarding is disabled, or the NAT masquerade rule names the wrong outbound interface. Check net.ipv4.ip_forward, confirm the interface with ip route, and make sure the FORWARD chain allows traffic from wg0.

How do I add a phone to my WireGuard server?

Generate a new key pair and assign an unused tunnel address, such as 10.8.0.3/32. Add it as a peer on the server and apply the change with wg syncconf. Build the client config, then show it as a QR code with qrencode and scan it in the WireGuard iOS or Android app.

Deploy your WireGuard server on a Nixcon VDS today.Learn more