WireGuard is a compact, modern, cross-platform VPN suited to secure remote access to servers and private networks. This guide builds a peer-to-site deployment on Ubuntu Server: every device receives a unique key pair, the server routes VPN traffic, and the firewall limits access.
A VPN does not replace application authorization. A user connected to the network should still reach only the services they need.
Example architecture and values
- The Ubuntu Server has a public IP or receives a forwarded UDP port.
- Its Internet interface is
eth0; replace this with the actual name. - WireGuard network:
10.66.66.0/24. - VPN server:
10.66.66.1; first client:10.66.66.2. - UDP port:
51820.
Find the outgoing interface with ip route get 1.1.1.1. If the server is behind a router, forward UDP 51820 to its LAN address and reserve that address in DHCP. Ordinary port forwarding does not work behind CGNAT; obtain a public IP, suitable IPv6, an intermediary VPS, or another tunnel design.
1. Update Ubuntu and install WireGuard
sudo apt update
sudo apt upgrade
sudo apt install wireguard qrencode
sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
qrencode is optional and used for phone enrollment. Before changing a remote firewall, keep a second SSH session open or ensure provider-console access so a mistake does not lock out administration.
2. Generate server keys
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo cat /etc/wireguard/server.pub
Only root should read the private key. Never send it through chat or email or commit it to Git. The public key can be shared with peers. Give every client its own pair so one device can be revoked independently.
3. Enable IP forwarding
Forwarding may not be needed when clients access only services on the VPN server itself. When the server is a gateway to the Internet or LAN, create:
sudo tee /etc/sysctl.d/70-wireguard-routing.conf > /dev/null <<'EOF'
net.ipv4.ip_forward = 1
EOF
sudo sysctl -p /etc/sysctl.d/70-wireguard-routing.conf
Run sysctl net.ipv4.ip_forward and expect 1. Enable IPv6 forwarding only after designing IPv6 addressing and firewall rules rather than copying IPv4 assumptions.
4. Create the server configuration
Create /etc/wireguard/wg0.conf:
[Interface]
Address = 10.66.66.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
# Client laptop
[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.66.66.2/32
Replace placeholders with real key values, then run sudo chmod 600 /etc/wireguard/wg0.conf. In WireGuard, AllowedIPs selects the peer for outbound traffic and acts as a source-address ACL on receipt. Never assign the same /32 to two clients.
To avoid embedding the server private key in the primary config, Ubuntu documentation demonstrates PostUp = wg set %i private-key /etc/wireguard/server.key. Either way, protect both key and configuration files with filesystem permissions.
5. Configure UFW and NAT
Allow SSH first and then open WireGuard:
sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw route allow in on wg0 out on eth0 from 10.66.66.0/24
sudo ufw route allow in on eth0 out on wg0 to 10.66.66.0/24
For clients to use the server as an Internet gateway, add this NAT block near the top of /etc/ufw/before.rules, before existing *filter tables:
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.66.66.0/24 -o eth0 -j MASQUERADE
COMMIT
Set DEFAULT_FORWARD_POLICY="ACCEPT" in /etc/default/ufw, then:
sudo ufw enable
sudo ufw reload
sudo ufw status verbose
Replace eth0 with the actual Internet interface. For LAN-only access where the LAN router has a return route to 10.66.66.0/24, routing without NAT preserves client addresses in logs; adapt routes and firewall rules to the real topology.
6. Create the first client
Generating keys on the client is preferable because its private key never leaves the device. On Linux:
umask 077
wg genkey > client.key
wg pubkey < client.key > client.pub
Add the client public key to its server [Peer] block. The client configuration is:
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.66.66.2/32
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = vpn.example.com:51820
AllowedIPs = 10.66.66.0/24
PersistentKeepalive = 25
PersistentKeepalive = 25 helps a client behind NAT maintain its mapping but is not necessary on every peer. Use dynamic DNS when the server public address changes.
7. Choose split tunnel or full tunnel
The configuration above is a split tunnel: only 10.66.66.0/24 uses the VPN. To reach a LAN, add its subnet, such as 192.168.10.0/24, to client AllowedIPs and permit the exact forwarding flow.
For a full tunnel, use AllowedIPs = 0.0.0.0/0, adding ::/0 only after IPv6 is configured. All client Internet traffic then crosses the server, requiring forwarding, NAT, DNS, and adequate capacity. It protects traffic on public Wi-Fi but adds load, latency, and operational responsibility.
8. Start and test the tunnel
sudo wg-quick up wg0
sudo wg show
ip address show wg0
sudo systemctl enable wg-quick@wg0
Connect the client, run ping 10.66.66.1, test private services, and inspect latest handshake and transfer in sudo wg show. With a full tunnel, verify the client's public IP and DNS path.
Use sudo systemctl reload wg-quick@wg0 to add peers with less disruption. Address, route, and hook changes may still require a full restart.
9. Add a phone securely with a QR code
qrencode -t ansiutf8 < phone.conf
In the mobile WireGuard application, create a tunnel from QR and scan it in a private administrative session. The QR contains the private key, so treat it as a password: do not screenshot it, attach it to a ticket, or retain temporary files unnecessarily.
10. Revoke a device
Remove the device's [Peer] block from the server and reload the service. Do not reuse keys for replacement hardware. If a server private key is exposed, create a new server pair and update every client; rotate only one peer when that peer's private key is compromised.
Troubleshooting
- No handshake: inspect UDP 51820, port forwarding, public IP or CGNAT, Endpoint, and public keys.
- Handshake but no ping: check
AllowedIPs, duplicate addresses, UFW, andip route. - Server reachable but no LAN or Internet: check IP forwarding, FORWARD rules, NAT, and return routes.
- IP works but names fail: inspect client DNS and resolver reachability through the tunnel.
- Some sites hang: test a lower MTU such as
MTU = 1380after confirming a path-MTU issue. - Mobile connection drops: apply
PersistentKeepalive = 25to the peer behind NAT.
sudo wg show
sudo journalctl -u wg-quick@wg0
ip route
sudo ufw status verbose
sudo ss -lunp | grep 51820
Security and operations checklist
- Every device has a unique key and VPN address.
- Private keys and configurations have restricted permissions.
- Firewall rules expose only necessary services to the VPN subnet.
- Public SSH is removed where administration can use the VPN.
- Peer owner, issue date, and revocation date are recorded.
- An encrypted server configuration backup exists off-host.
- Ubuntu is patched and unexpected handshakes are monitored.
- Peer revocation and configuration recovery are tested.
Conclusion
A sound WireGuard VPN is more than a successful handshake. It needs per-device keys, precise AllowedIPs, restrictive firewall rules, intentional routing or NAT, and a clear revocation process. Start with a small split tunnel, verify each flow, and expand to LAN or full-tunnel access only when required.




No comments yet. Be the first to share your thoughts.