WireGuard Server Setup on Linux (wg0, NAT, Peers)
12 min read - June 22, 2026

Practical WireGuard server setup for Linux: keys, wg0.conf, NAT, peer management with syncconf, and the troubleshooting commands sysadmins actually use.
Run a WireGuard server when you want private access to hosted infrastructure without exposing admin ports to the public internet. It uses public-key authentication, runs inside the Linux kernel since 5.6, and stays out of the way once it's up. This post covers when to use a WireGuard server, how to bring up wg0, and how to keep peers working day to day.
When to use a WireGuard server
Three patterns cover most setups: remote access, server-to-server links, and site-to-site routing.
For remote access, run the server on a VPS or dedicated host and route admin traffic through the tunnel. Set AllowedIPs on each peer to the private management subnet (something like 10.0.0.0/24) so only traffic for internal systems uses the tunnel. Everything else stays on the user's local connection. If users connect from home or mobile networks, add PersistentKeepalive = 25 on the client side to keep NAT sessions from being torn down.
For server-to-server links, keep AllowedIPs narrow. Usually a single /32 or a small backend subnet. That avoids pulling unrelated traffic into the tunnel and keeps routing predictable.
Site-to-site is different. The WireGuard host acts as a gateway between subnets, so IP forwarding has to be enabled and the NAT rules have to send return traffic out the right interface.
| Pattern | AllowedIPs scope | Best fit | Setup complexity |
|---|---|---|---|
| Remote access | Private subnets, e.g. 10.0.0.0/24 | Admin and developer access | Low |
| Server-to-server | Specific IPs or backend subnet | Point-to-point host links | Low to medium |
| Site-to-site | Entire remote LAN, e.g. /24 | Gateway-to-gateway routing | Medium to high |
| Private service access | Internal subnet only (split-tunnel) | Backend service isolation | Medium |
Server setup
The server holds the private key, listens on UDP 51820 by default, and terminates the tunnel. The same base setup works for all three patterns above.
Keys and wg0.conf
Generate the server keypair:
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.keyLock down the private key:
sudo chmod 600 /etc/wireguard/server_private.keyThe private key stays on the server. The public key is what you hand to peers.
Create /etc/wireguard/wg0.conf:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o <PUBLIC_IFACE> -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o <PUBLIC_IFACE> -j MASQUERADE
[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.8.0.2/32Find the outbound interface to drop into <PUBLIC_IFACE> with:
ip -o -4 route show to default | awk '{print $5}'Forwarding, firewall, and NAT
Open the listening port:
sudo ufw allow 51820/udpEnable IP forwarding by adding this line to /etc/sysctl.conf:
net.ipv4.ip_forward = 1Apply without rebooting:
sudo sysctl -pThe PostUp and PostDown lines in wg0.conf add and remove the NAT masquerade rule when the interface comes up or down. Without them, return traffic from the LAN never makes it back to the peer.
Bringing the tunnel up
wg-quick handles the interface, routing, and the PostUp/PostDown hooks in one command:
sudo wg-quick up wg0For automatic startup after reboot, enable the systemd unit:
sudo systemctl enable --now wg-quick@wg0Check status with:
sudo wg showA recent latest handshake line confirms the tunnel is working. If it looks stale or empty, check the firewall, the keys on both sides, and the peer's AllowedIPs.
Adding and removing peers
Each peer needs its own keypair. Generate it on the client, then add the peer's public key to wg0.conf in a new [Peer] block with an AllowedIPs entry that assigns its tunnel IP.
Use /32 for a single device:
AllowedIPs = 10.8.0.3/32That stops one peer from claiming addresses assigned to another. For split-tunnel access, list only the private subnets that should go through the tunnel, for example AllowedIPs = 10.8.0.0/24.
Apply config changes without dropping active sessions:
sudo wg syncconf wg0 <(wg-quick strip wg0)Removing a peer works the same way. Delete its [Peer] block from wg0.conf and run syncconf again.
Troubleshooting
If a peer connects but can't reach anything on the other side, the cause is usually one of four things:
- IP forwarding is off
- The NAT masquerade rule is missing
- The outbound interface in the NAT rule is wrong
AllowedIPsdoesn't include the destination
Check forwarding:
cat /proc/sys/net/ipv4/ip_forwardShould return 1. If it returns 0, the sysctl change didn't apply or wasn't saved.
Check the NAT rule and outbound interface:
sudo iptables -t nat -L POSTROUTING
ip route get 1.1.1.1The second command shows the actual outbound interface name, such as ens3, enp1s0, or eth0. That has to match the interface in the MASQUERADE rule.
If the handshake itself is missing, check that UDP 51820 is open at the firewall and at any upstream provider, and that both sides have the right public key for the other.
For peers behind residential or mobile NAT that drops idle UDP sessions, set PersistentKeepalive = 25 on the client.
Key rotation and pre-shared keys
For tunnels that stay up for months, rotate keys once a year or so. Generate a new keypair, update both ends, and apply with wg syncconf. Don't reuse a private key across two peers. That creates routing conflicts and breaks delivery between them.
For an extra layer on top of public-key auth, add a pre-shared key per peer:
wg genpskAdd the result as PresharedKey = <PSK> in the [Peer] block on both sides. WireGuard mixes the PSK into the handshake, so an attacker who somehow compromises one of the asymmetric keys still can't decrypt traffic without it.
Useful day-to-day commands:
| Command | Purpose |
|---|---|
wg show | Peers, handshakes, traffic counters |
wg show wg0 transfer | Byte counters for wg0 |
wg show all dump | Machine-parseable output for monitoring scripts |
wg syncconf wg0 <(wg-quick strip wg0) | Apply config changes without dropping sessions |
wg genpsk | Generate a pre-shared key |
If you need a stable, publicly reachable host to terminate WireGuard tunnels and route private traffic into your infrastructure, take a look at FDC's unmetered VPS plans.

Digital eye strain: How to protect your vision in a screen-heavy world
Staring at screens all day? Learn how to reduce digital eye strain with proven techniques and tools. This guide is essential for remote workers, developers, and anyone in tech.
4 min read - May 21, 2025
Why it's important to have a powerful and unmetered VPS
8 min read - May 9, 2025

Have questions or need a custom solution?
Flexible options
Global reach
Instant deployment
Flexible options
Global reach
Instant deployment