WireGuard Full Documentation
This guide explains how to install and configure a WireGuard VPN server on Ubuntu,
connect clients such as Windows computers and Android phones, generate QR codes,
manage multiple peers, configure routing, NAT, and port forwarding.
Introduction to WireGuard
WireGuard is a modern VPN protocol that creates an encrypted tunnel between devices.
It uses public and private keys instead of usernames and passwords.
A typical setup looks like this:
Internet
|
|
Ubuntu Server (WireGuard Server)
Public IP: AWS EC2 Public IP
WireGuard IP: 10.0.0.1
|
| Encrypted VPN Tunnel
|
Windows / Android / MikroTik Clients
10.0.0.2, 10.0.0.3, etc.
Install WireGuard on Ubuntu Server
sudo apt update
sudo apt install wireguard -y
WireGuard configuration files are stored in:
/etc/wireguard/
The main server configuration file will be:
/etc/wireguard/wg0.conf
Check WireGuard Version
wg --version
Example output:
wireguard-tools v1.x.x
Generate Server Keys
WireGuard uses two keys:
Private Key → Secret, never share
Public Key → Shared with clients
cd /etc/wireguard
sudo wg genkey | sudo tee server_private.key
sudo cat server_private.key | wg pubkey | sudo tee server_public.key
Check the keys:
sudo cat /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_public.key
The private key stays on the server.
The public key is shared with clients.
Create WireGuard Server Configuration
sudo nano /etc/wireguard/wg0.conf
Example configuration:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o ens5 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o ens5 -j MASQUERADE
Replace:
SERVER_PRIVATE_KEY
with:
sudo cat /etc/wireguard/server_private.key
Replace:
ens5
with your internet interface name.
For AWS EC2, it is commonly:
ens5
Check yours:
ip addr
Enable IP Forwarding
IP forwarding allows Ubuntu to route traffic between WireGuard clients and the internet.
sudo nano /etc/sysctl.conf
Add:
net.ipv4.ip_forward=1
Apply changes:
sudo sysctl -p
Verify:
cat /proc/sys/net/ipv4/ip_forward
Expected output:
1
Firewall and NAT Configuration
Allow WireGuard Port
sudo ufw allow 51820/udp
sudo ufw reload
AWS users must also allow the port in Security Group:
Type: Custom UDP Rule
Port: 51820
Source: 0.0.0.0/0
NAT Explanation
The MASQUERADE rule allows VPN clients to access the internet using the Ubuntu server public IP.
iptables -t nat -A POSTROUTING -o ens5 -j MASQUERADE
Example:
Before NAT:
Client: 10.0.0.2 → Internet
After NAT:
AWS Public IP → Internet
Start and Enable WireGuard Service
sudo systemctl start wg-quick@wg0
sudo systemctl enable wg-quick@wg0
Check status:
systemctl status wg-quick@wg0
Check interface:
sudo wg show
Successful output should show:
interface: wg0
public key: SERVER_PUBLIC_KEY
listening port: 51820
Generate Client Keys
Every WireGuard client must have its own unique key pair.
Never reuse the same private key for multiple devices.
Example clients:
Windows PC
Android Phone
Laptop
MikroTik Router
Create Client Directory
mkdir -p ~/wireguard-clients
cd ~/wireguard-clients
Generate Client 1 Keys
wg genkey | tee client1_private.key
cat client1_private.key | wg pubkey | tee client1_public.key
View keys:
cat client1_private.key
cat client1_public.key
Example:
client1_private.key
xxxxxxxxxxxxxxxxxxxxxxxxxxxx=
client1_public.key
yyyyyyyyyyyyyyyyyyyyyyyyyyyy=
The private key goes to the client device.
The public key is added to the Ubuntu server.
Add Client Peer to WireGuard Server
A peer tells the server:
"Allow this device to connect using this public key and assign it this VPN IP."
Edit Server Configuration
sudo nano /etc/wireguard/wg0.conf
Add the client at the bottom:
[Peer]
# Client 1
PublicKey = CLIENT1_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
Replace:
CLIENT1_PUBLIC_KEY
with:
cat ~/wireguard-clients/client1_public.key
Restart WireGuard:
sudo systemctl restart wg-quick@wg0
Configure WireGuard Client on Windows
Install Application
Download and install the WireGuard Windows application.
Create Tunnel
WireGuard Application
↓
Add Tunnel
↓
Add empty tunnel
The application automatically creates a private key.
Example configuration:
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.0.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = SERVER_PUBLIC_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Configuration Explanation
Address = 10.0.0.2/24
Client VPN address
PublicKey
Ubuntu server public key
Endpoint
Ubuntu server public IP and port
AllowedIPs = 0.0.0.0/0
Send all internet traffic through VPN
PersistentKeepalive = 25
Keeps tunnel alive behind NAT
Add Windows Public Key to Server
Copy the public key from WireGuard Windows application.
Add it to Ubuntu:
sudo nano /etc/wireguard/wg0.conf
[Peer]
PublicKey = WINDOWS_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
Restart:
sudo systemctl restart wg-quick@wg0
Configure WireGuard Client on Android
Android uses the same configuration as Windows.
The easiest method is importing using a QR code.
Install:
WireGuard App
Google Play Store
Create Client Configuration File
nano ~/wireguard-clients/client1.conf
Example:
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY
Address = 10.0.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = YOUR_SERVER_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Insert client private key:
cat ~/wireguard-clients/client1_private.key
Insert server public key:
sudo wg show wg0 public-key
Generate WireGuard QR Code
Install qrencode
sudo apt update
sudo apt install qrencode -y
Display QR Code in Terminal
qrencode -t ansiutf8 < ~/wireguard-clients/client1.conf
Open WireGuard Android application:
WireGuard App
↓
Add Tunnel
↓
Scan from QR Code
Create QR Image File
qrencode -o client1.png < ~/wireguard-clients/client1.conf
The PNG contains the complete WireGuard configuration.
WARNING:
The QR code contains the client private key.
Anyone who scans it can connect as that client.
Verify Client Connection
Activate the client tunnel, then check Ubuntu:
sudo wg show
Successful connection:
peer: CLIENT_PUBLIC_KEY
endpoint: CLIENT_IP:PORT
latest handshake: few seconds ago
transfer: received, sent
A handshake proves that the encrypted tunnel is established.
Managing Multiple WireGuard Peers
WireGuard supports multiple clients connected to one server.
Each device must have its own private key, public key, and VPN IP address.
Example network design:
WireGuard Server
wg0: 10.0.0.1/24
Client 1 - Windows PC
10.0.0.2
Client 2 - Android Phone
10.0.0.3
Client 3 - Laptop
10.0.0.4
Client 4 - MikroTik Router
10.0.0.5
Never assign the same VPN IP to two different clients.
Client IP Allocation Planning
A simple method is assigning addresses sequentially.
Server:
10.0.0.1
Windows:
10.0.0.2
Android:
10.0.0.3
Laptop:
10.0.0.4
Router:
10.0.0.5
Server configuration example:
[Peer]
# Windows PC
PublicKey = WINDOWS_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
[Peer]
# Android Phone
PublicKey = ANDROID_PUBLIC_KEY
AllowedIPs = 10.0.0.3/32
[Peer]
# Laptop
PublicKey = LAPTOP_PUBLIC_KEY
AllowedIPs = 10.0.0.4/32
Recommended Client File Organization
~/wireguard-clients/
│
├── windows.conf
├── windows_private.key
├── windows_public.key
│
├── android.conf
├── android_private.key
├── android_public.key
│
└── laptop.conf
├── laptop_private.key
└── laptop_public.key
Using names like windows, android, and office-laptop is easier than using only client1, client2.
Full Tunnel VPN Configuration
A full tunnel means all client internet traffic goes through the WireGuard server.
Client configuration:
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = SERVER_IP:51820
AllowedIPs = 0.0.0.0/0
Traffic flow:
Client
10.0.0.2
|
v
WireGuard Server
10.0.0.1
|
v
Internet
The internet sees the Ubuntu server public IP instead of the client's real IP.
Split Tunnel Configuration
Split tunnel sends only VPN traffic through WireGuard.
Normal internet traffic uses the client's local connection.
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 10.0.0.0/24
Example:
Access server:
10.0.0.1 → VPN tunnel
Google:
8.8.8.8 → Normal internet
Port Forwarding to WireGuard Clients
You can expose a service running on a WireGuard client through the Ubuntu server.
Example:
Internet User
|
| Port 80
v
Ubuntu Server
Public IP
|
| WireGuard
v
Client PC
10.0.0.2:80
Enable Forwarding
cat /proc/sys/net/ipv4/ip_forward
Expected:
1
Add DNAT Rule
sudo iptables -t nat -A PREROUTING \
-i ens5 \
-p tcp \
--dport 80 \
-j DNAT \
--to-destination 10.0.0.2:80
Explanation:
ens5
Internet interface
80
Incoming HTTP port
10.0.0.2:80
Destination client web server
Allow Forward Traffic
sudo iptables -A FORWARD \
-p tcp \
-d 10.0.0.2 \
--dport 80 \
-m conntrack \
--ctstate NEW,ESTABLISHED,RELATED \
-j ACCEPT
Allow Return Traffic
sudo iptables -A FORWARD \
-p tcp \
-s 10.0.0.2 \
--sport 80 \
-m conntrack \
--ctstate ESTABLISHED,RELATED \
-j ACCEPT
The first rule allows requests to reach the client.
The second allows responses to return.
AWS EC2 WireGuard Configuration
When running WireGuard on AWS, there are two firewalls:
1. AWS Security Group
2. Ubuntu Firewall (UFW)
AWS Security Group
Inbound Rule:
Type:
Custom UDP
Port:
51820
Source:
0.0.0.0/0
For port forwarding:
HTTP TCP 80
Source: 0.0.0.0/0
Check AWS Interface
ip addr
AWS Ubuntu usually uses:
ens5
MikroTik WireGuard Client
WireGuard support requires RouterOS 7 or newer.
RouterOS 6 does not include native WireGuard.
Create Interface
/interface wireguard
add name=wg1
Add IP Address
/ip address
add address=10.0.0.5/24 interface=wg1
Add Ubuntu Peer
/interface wireguard peers
add interface=wg1 \
public-key="SERVER_PUBLIC_KEY" \
endpoint-address=SERVER_IP \
endpoint-port=51820 \
allowed-address=0.0.0.0/0 \
persistent-keepalive=25
Add MikroTik Public Key to Ubuntu
[Peer]
PublicKey = MIKROTIK_PUBLIC_KEY
AllowedIPs = 10.0.0.5/32
WireGuard Troubleshooting
When WireGuard does not connect, troubleshoot in the following order:
1. Check service status
2. Check keys
3. Check firewall
4. Check routing
5. Check handshake
6. Check NAT
Check WireGuard Service Status
sudo systemctl status wg-quick@wg0
Successful output:
Active: active (exited)
If failed:
Active: failed (Result: exit-code)
View detailed error:
sudo journalctl -u wg-quick@wg0 -n 50
Check WireGuard Interface
sudo wg show
Example working output:
interface: wg0
public key: SERVER_PUBLIC_KEY
listening port: 51820
peer: CLIENT_PUBLIC_KEY
endpoint: CLIENT_IP:PORT
latest handshake: 20 seconds ago
transfer: received 1.2 MiB, sent 500 KiB
Important values:
latest handshake
Shows whether the client is connected.
transfer
Shows whether data is moving.
Problem: No Handshake
If you see:
latest handshake: never
Check the following.
1. Check Server Port
sudo ss -ulnp | grep 51820
Expected:
udp UNCONN 0 0 0.0.0.0:51820
2. Check AWS Security Group
Inbound:
UDP
51820
Source: 0.0.0.0/0
3. Check UFW
sudo ufw status
Allow WireGuard:
sudo ufw allow 51820/udp
4. Check Client Endpoint
Endpoint = SERVER_PUBLIC_IP:51820
Common mistakes:
Wrong:
Endpoint = 10.0.0.1:51820
Correct:
Endpoint = AWS_PUBLIC_IP:51820
UFW Permission Error
A common error:
/usr/bin/wg-quick: line 295:
/usr/sbin/ufw: Permission denied
Cause:
wg-quick is trying to execute UFW commands,
but UFW permissions are incorrect.
Check UFW permissions:
ls -l /usr/sbin/ufw
Correct permission example:
-rwxr-xr-x
Fix:
sudo chmod 755 /usr/sbin/ufw
Restart:
sudo systemctl restart wg-quick@wg0
Check IP Forwarding
cat /proc/sys/net/ipv4/ip_forward
Correct:
1
Enable permanently:
sudo nano /etc/sysctl.conf
Add:
net.ipv4.ip_forward=1
Apply:
sudo sysctl -p
Check iptables Rules
sudo iptables -L -v
Check NAT:
sudo iptables -t nat -L -v
Expected:
MASQUERADE
out-interface ens5
Make iptables Rules Persistent
iptables rules disappear after reboot unless saved.
Install persistence package:
sudo apt install iptables-persistent -y
Save current rules:
sudo netfilter-persistent save
Restore automatically:
sudo systemctl enable netfilter-persistent
Check Logs
WireGuard Logs
sudo journalctl -u wg-quick@wg0
Follow live:
sudo journalctl -u wg-quick@wg0 -f
Kernel Logs
sudo dmesg | grep wireguard
WireGuard Security Best Practices
1. Protect Private Keys
sudo chmod 600 /etc/wireguard/*.key
Never share:
server_private.key
client_private.key
Only share:
public keys
2. Use Unique Keys
Wrong:
Windows + Android
same private key
Correct:
Windows
private key 1
Android
private key 2
3. Remove Lost Devices
If a phone or laptop is lost, remove its peer.
Edit:
sudo nano /etc/wireguard/wg0.conf
Delete:
[Peer]
PublicKey = LOST_DEVICE_KEY
AllowedIPs = 10.0.0.x/32
Restart:
sudo systemctl restart wg-quick@wg0
Backup WireGuard Configuration
Backup folder:
sudo cp -r /etc/wireguard ~/wireguard-backup
Compress:
tar -czvf wireguard-backup.tar.gz /etc/wireguard
Useful WireGuard Commands
sudo systemctl start wg-quick@wg0
sudo systemctl stop wg-quick@wg0
sudo systemctl restart wg-quick@wg0
sudo systemctl enable wg-quick@wg0
sudo wg show
sudo wg-quick up wg0
sudo wg-quick down wg0
Production Deployment Checklist
✓ Ubuntu updated
✓ WireGuard installed
✓ Server keys generated
✓ Client keys generated
✓ UDP 51820 opened
✓ AWS Security Group configured
✓ IP forwarding enabled
✓ NAT configured
✓ Firewall tested
✓ Clients connected
✓ Handshake verified
✓ Private keys protected
✓ Configuration backed up
Final Network Example
Internet
|
|
AWS Ubuntu Server
|
Public IP
|
WireGuard wg0
10.0.0.1
|
---------------------
| | |
Windows Android MikroTik
10.0.0.2 10.0.0.3 10.0.0.5
This completes the WireGuard server and client deployment guide.