How to Use WireGuard on Linux: From Installation to Multi-Peer Setup
WireGuard is a modern VPN protocol built directly into the Linux kernel since version 5.6. It replaces older solutions like OpenVPN and IPsec with something radically simpler: a small, auditable codebase, state-of-the-art cryptography, and kernel-level performance. WireGuard connections are fast, stable, and roam seamlessly between networks — making it the go-to choice for anyone who needs a serious VPN layer in 2026.
In this guide you will go from zero to a fully working WireGuard setup: a server that forwards traffic for multiple peers, clients that can bring the tunnel up or down with a single command, and enough diagnostic knowledge to fix problems when they arise.
Deeper reading: If you want to understand why WireGuard is secure — the Noise protocol handshake, ChaCha20-Poly1305 encryption, and Curve25519 key exchange — see our companion article How WireGuard Works.
1. Installation
WireGuard is included in the mainline kernel, so installation on modern distributions is straightforward.
Ubuntu / Debian
sudo apt update
sudo apt install -y wireguard wireguard-tools
On Ubuntu 20.04 and earlier you may need the backports PPA:
sudo add-apt-repository ppa:wireguard/wireguard
sudo apt update
sudo apt install -y wireguard
Fedora / CentOS / RHEL
On Fedora, WireGuard is in the default repositories:
sudo dnf install -y wireguard-tools
On CentOS 8 / RHEL 8 you need to enable EPEL and the ELRepo kernel module:
sudo dnf install -y epel-release elrepo-release
sudo dnf install -y kmod-wireguard wireguard-tools
On CentOS 9 Stream and RHEL 9, WireGuard is already built into the kernel — only the tools package is needed:
sudo dnf install -y wireguard-tools
Arch Linux
sudo pacman -S wireguard-tools
Verify the install
wg --version
# wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/
If the command returns a version, the userspace tools are ready. The kernel module loads automatically when you bring an interface up.
2. Key Generation
WireGuard uses Curve25519 public-key cryptography. Every peer — server and client alike — needs its own key pair. Keys are just base64 strings; there are no certificate authorities or revocation lists.
Generate the server keys first:
# Create a private key and derive the public key in one pipeline
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
# Lock down permissions — private keys must never be world-readable
chmod 600 /etc/wireguard/server_private.key
Print the values so you can paste them into config files:
cat /etc/wireguard/server_private.key
cat /etc/wireguard/server_public.key
Repeat for each client. You can do this on the server and distribute keys securely, or generate on the client device and only share the public key:
wg genkey | tee client1_private.key | wg pubkey > client1_public.key
wg genkey | tee client2_private.key | wg pubkey > client2_public.key
chmod 600 client1_private.key client2_private.key
3. Server Configuration
Enable IP forwarding
Before writing the WireGuard config, enable packet forwarding in the kernel. Without this, the server will receive packets from peers but refuse to route them onward:
# Apply immediately (survives until next reboot)
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1
# Make persistent across reboots
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf
Write /etc/wireguard/wg0.conf
Replace the placeholder values with your actual keys. The example below uses 10.0.0.0/24 as the VPN subnet and assumes your server's public internet interface is eth0 — adjust PostUp/PostDown if your interface is named differently (check with ip link show):
[Interface]
# The private IP address this server uses inside the VPN tunnel
Address = 10.0.0.1/24
# Port WireGuard listens on — open this in your firewall
ListenPort = 51820
# Paste your server private key here
PrivateKey = <SERVER_PRIVATE_KEY>
# NAT masquerade: forward peer traffic through the server's internet interface
PostUp = iptables -A FORWARD -i %i -j ACCEPT; \
iptables -A FORWARD -o %i -j ACCEPT; \
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; \
iptables -D FORWARD -o %i -j ACCEPT; \
iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# --- Peer: Client 1 ---
[Peer]
PublicKey = <CLIENT1_PUBLIC_KEY>
# The IP address assigned to this client inside the VPN
AllowedIPs = 10.0.0.2/32
# --- Peer: Client 2 ---
[Peer]
PublicKey = <CLIENT2_PUBLIC_KEY>
AllowedIPs = 10.0.0.3/32
Lock down the config file:
sudo chmod 600 /etc/wireguard/wg0.conf
Open the firewall port
# UFW (Ubuntu/Debian)
sudo ufw allow 51820/udp
# firewalld (Fedora/CentOS)
sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --reload
# iptables directly
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT
4. Client Configuration
On each client machine, create /etc/wireguard/wg0.conf. The client config is structurally identical to the server config — it has an [Interface] block for its own identity and a [Peer] block pointing at the server:
[Interface]
# This client's private IP inside the VPN
Address = 10.0.0.2/24
# This client's private key
PrivateKey = <CLIENT1_PRIVATE_KEY>
# Optional: use the VPN as the DNS resolver
DNS = 1.1.1.1
[Peer]
# The server's public key
PublicKey = <SERVER_PUBLIC_KEY>
# Server's public IP (or hostname) and port
Endpoint = <SERVER_PUBLIC_IP>:51820
# Route all internet traffic through the VPN
# Use "10.0.0.0/24" here instead to only route VPN subnet traffic (split tunnel)
AllowedIPs = 0.0.0.0/0, ::/0
# Keep the tunnel alive through NAT — send a keepalive every 25 seconds
PersistentKeepalive = 25
Set permissions:
sudo chmod 600 /etc/wireguard/wg0.conf
5. Bringing the Tunnel Up
wg-quick is a convenience wrapper around wg and ip that handles interface creation, routing, and iptables rules automatically.
# Start the tunnel
sudo wg-quick up wg0
# Stop the tunnel
sudo wg-quick down wg0
Enable on boot with systemd
# Enable auto-start at boot
sudo systemctl enable wg-quick@wg0
# Start it now without rebooting
sudo systemctl start wg-quick@wg0
# Check status
sudo systemctl status wg-quick@wg0
To disable:
sudo systemctl disable wg-quick@wg0
sudo systemctl stop wg-quick@wg0
6. Multi-Peer Setup
WireGuard's peer model is flat — there is no hierarchy between server and clients. The "server" is just a peer that happens to have ListenPort set and acts as a router. Adding more clients is as simple as adding more [Peer] blocks to the server config and assigning each one a unique IP in the VPN subnet.
Adding a third client
Generate keys for the new client:
wg genkey | tee client3_private.key | wg pubkey > client3_public.key
Add a block to /etc/wireguard/wg0.conf on the server:
[Peer]
PublicKey = <CLIENT3_PUBLIC_KEY>
AllowedIPs = 10.0.0.4/32
You can reload the running interface without a full restart using wg syncconf or wg addpeer:
# Hot-reload from the config file (no downtime for existing peers)
sudo wg syncconf wg0 <(wg-quick strip wg0)
The wg-quick strip command strips wg-quick-specific directives (Address, PostUp, etc.) before passing the config to wg syncconf, which only understands native WireGuard options.
Subnet allocation tip
Keep a simple allocation table so you don't accidentally reuse addresses:
| Peer | VPN IP | |----------|-------------| | Server | 10.0.0.1 | | Client 1 | 10.0.0.2 | | Client 2 | 10.0.0.3 | | Client 3 | 10.0.0.4 |
For large deployments, consider a /16 subnet (10.0.0.0/16) to give yourself room to grow.
7. Verifying the Tunnel
wg show
On the server, wg show is your first diagnostic tool:
sudo wg show
Sample output:
interface: wg0
public key: <SERVER_PUBLIC_KEY>
private key: (hidden)
listening port: 51820
peer: <CLIENT1_PUBLIC_KEY>
endpoint: 203.0.113.42:54321
allowed ips: 10.0.0.2/32
latest handshake: 14 seconds ago
transfer: 1.23 MiB received, 4.56 MiB sent
What to look for:
latest handshake— if this is missing or very stale (> 3 minutes withPersistentKeepalive = 25), the peer is not connected.transfer— bytes are moving, which confirms data is flowing.endpoint— the peer's current public IP and ephemeral port. WireGuard auto-updates this when a peer roams.
Ping across the tunnel
From a connected client, ping the server's VPN IP:
ping 10.0.0.1
From the server, ping a client:
ping 10.0.0.2
Verify traffic routing
If you used AllowedIPs = 0.0.0.0/0 on the client, all traffic should now egress through the server. Confirm with:
curl https://ifconfig.me
The returned IP should be the server's public IP, not your local ISP address.
8. Troubleshooting
Tunnel comes up but no traffic flows
Check IP forwarding. This is the most common cause:
sysctl net.ipv4.ip_forward
# Should return: net.ipv4.ip_forward = 1
If it returns 0, re-apply the sysctl settings from Section 3.
Check iptables NAT rules:
sudo iptables -t nat -L POSTROUTING -n -v
You should see a MASQUERADE rule for eth0. If not, the PostUp script did not run — bring the interface down and up again:
sudo wg-quick down wg0 && sudo wg-quick up wg0
Handshake never completes
This almost always means the UDP port is blocked by a firewall. Verify from the client side:
# Test UDP reachability (requires nmap)
sudo nmap -sU -p 51820 <SERVER_PUBLIC_IP>
On the server, confirm the port is actually listening:
sudo ss -ulnp | grep 51820
Make sure both your OS firewall (UFW/firewalld/iptables) and any cloud security group / VPS firewall panel are open for 51820/udp.
MTU issues causing dropped packets or slow connections
WireGuard adds overhead to each packet (headers + encryption). If the MTU on wg0 is too large, packets get fragmented or silently dropped. The default wg-quick MTU is usually fine, but if you see issues with large transfers while small pings work:
# Check current MTU
ip link show wg0
# Set a lower MTU (1380 is a safe choice for most networks)
sudo ip link set wg0 mtu 1380
To make the MTU persistent, add it to the [Interface] section of your config:
[Interface]
Address = 10.0.0.2/24
PrivateKey = <CLIENT1_PRIVATE_KEY>
MTU = 1380
DNS leaks
If you set DNS = 1.1.1.1 in the client config but DNS queries still go out over the unencrypted interface, check that systemd-resolved is active and that wg-quick is correctly applying the DNS setting:
# See which DNS is currently active
resolvectl status
# Force flush the cache
sudo resolvectl flush-caches
Logging
WireGuard intentionally produces minimal kernel logs to avoid leaking metadata. To enable debug output temporarily:
echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control
# Watch output
sudo dmesg -w | grep wireguard
Remember to disable debug logging when you are done:
echo module wireguard -p | sudo tee /sys/kernel/debug/dynamic_debug/control
FastSox and WireGuard
FastSox uses WireGuard as the foundation of its VPN layer, extended with intelligent routing, multi-gateway failover, and a subscription-based access model. If you prefer a managed solution over running your own WireGuard server, FastSox handles key distribution, gateway provisioning, and routing policy automatically — you connect, it works.
For a deep dive into the cryptographic protocol that makes all of this possible, read How WireGuard Works.
FastSox is developed by OneDotNet Ltd.
Summary
Here is a quick reference for the commands used in this guide:
| Task | Command |
|------|---------|
| Install (Ubuntu) | sudo apt install wireguard wireguard-tools |
| Generate private key | wg genkey |
| Derive public key | wg pubkey < private.key |
| Bring tunnel up | sudo wg-quick up wg0 |
| Bring tunnel down | sudo wg-quick down wg0 |
| Enable on boot | sudo systemctl enable wg-quick@wg0 |
| Show status | sudo wg show |
| Hot-reload config | sudo wg syncconf wg0 <(wg-quick strip wg0) |
WireGuard's simplicity is its greatest strength. A complete, production-ready VPN fits in a handful of config lines — and once you understand the key-exchange model, adding new peers takes under a minute. That same simplicity is why FastSox chose WireGuard as its tunneling backbone.
Related Articles
Best Practices to Secure a Linux Server in 2026
A comprehensive, checklist-style guide to hardening a Linux server in 2026. Covers SSH hardening, firewalls, fail2ban, automatic updates, user management, kernel sysctl tuning, file system security, audit logging, and VPN-only management access.
How to Bootstrap a Secure Linux Setup Using iptables and ufw
A practical checklist for getting a fresh Ubuntu or Debian machine to a defensible firewall baseline — covering ufw for fast setup, iptables for precision control, common attack mitigations, nftables, WireGuard rules, and how to verify your ruleset.
How to Optimize TCP Traffic on Windows and Linux
A practical guide to tuning TCP congestion control, buffer sizes, and MTU on both Linux and Windows — so your VPN or proxy connection reaches its full potential.