Secure, production-ready setup for a WireGuard VPN server on any Linux server (Ubuntu/Debian recommended), with full-tunnel routing and real-world troubleshooting.
- Linux server (Ubuntu/Debian recommended)
- Root or sudo access
- Public IP address
- Client device (Linux / macOS / Windows / Android / iOS)
Some networks block UDP (e.g., port 51820).
To improve compatibility, this guide uses UDP port 443.
sudo apt update && sudo apt upgrade -ysudo adduser deployer
sudo usermod -aG sudo deployerRun from your local machine:
ssh-copy-id deployer@SERVER_IP
β οΈ Ensure you can SSH asdeployerbefore disabling root login.
sudo nano /etc/ssh/sshd_configPermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yessudo systemctl restart sshsu - deployersudo apt install wireguard -ysudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
wg genkey | sudo tee /etc/wireguard/server_private.key > /dev/null
sudo cat /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key > /dev/null
sudo chmod 600 /etc/wireguard/server_private.keyecho "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --systemDetect interface:
INTERFACE=$(ip route get 8.8.8.8 | awk '{print $5}')Apply rules:
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -A INPUT -p udp --dport 443 -j ACCEPT
sudo iptables -A FORWARD -i wg0 -o $INTERFACE -j ACCEPT
sudo iptables -A FORWARD -i $INTERFACE -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o $INTERFACE -j MASQUERADEPersist rules:
sudo apt install iptables-persistent -y
sudo netfilter-persistent savesudo nano /etc/wireguard/wg0.conf[Interface]
Address = 10.10.0.1/24
ListenPort = 443
PrivateKey = <SERVER_PRIVATE_KEY>
PostUp = iptables -A FORWARD -i wg0 -o $INTERFACE -j ACCEPT; iptables -A FORWARD -i $INTERFACE -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o $INTERFACE -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o $INTERFACE -j ACCEPT; iptables -D FORWARD -i $INTERFACE -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -D POSTROUTING -s 10.10.0.0/24 -o $INTERFACE -j MASQUERADEInsert:
sudo cat /etc/wireguard/server_private.keysudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wgVerify:
ip a show wg0On client machine:
wg genkey | tee client_private.key | wg pubkey > client_public.keyAdd to server:
sudo nano /etc/wireguard/wg0.conf[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.10.0.2/32Restart:
sudo systemctl restart wg-quick@wg0Get server public key:
cat /etc/wireguard/server_public.key[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.10.0.2/24
DNS = 1.1.1.1
# MTU (Maximum Transmission Unit)
# Default WireGuard MTU is usually ~1420
# Use lower values if you experience:
# - slow connections
# - some websites not loading
# - random timeouts
#
# Recommended values:
# 1420 β default (best performance if network is clean)
# 1380 β good for restricted networks / VPN over VPN (recommended)
# 1280 β fallback for very restrictive networks
MTU = 1380
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = SERVER_IP:443
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25sudo apt install wireguard -y
nano ~/wg-client.conf
sudo wg-quick up ~/wg-client.conf- Install WireGuard from App Store
- Add tunnel β βAdd Empty Tunnelβ
- Paste config β Activate
- Install WireGuard from official site
- Import tunnel from file
- Activate
- Install WireGuard app
- Add tunnel β Create from scratch or import
- Activate
- Install WireGuard app
- Add tunnel β Create from scratch or import
- Activate
sudo apt install qrencode -y
qrencode -t ansiutf8 < client.confsudo wg
curl ifconfig.me
ping 8.8.8.8
ping google.comExpected:
- Handshake present
- Public IP = server IP
- Internet works
sudo tcpdump -ni any udp port 443- No packets β ISP/network blocking UDP
- Packets present β config issue
sysctl net.ipv4.ip_forward
iptables -t nat -Lping 8.8.8.8
ping google.com
β οΈ If DNS fails, your system may not applyresolvconfautomatically.
Symptoms:
- No handshake
- No packets in tcpdump
Solution:
ListenPort = 443
Endpoint = SERVER_IP:443- Never commit private keys
- Use placeholders:
<SERVER_PRIVATE_KEY>
<CLIENT_PRIVATE_KEY>
- Rotate keys periodically
- Restrict SSH access
- Subnet:
10.10.0.0/24 - Each client:
/32 - Avoid mixing firewall tools
MIT