WireGuard VPN Server Setup on Linux (Step-by-Step Guide)

wireguard vpn server linux - custom-wg-featured.png

WireGuard is a small, fast VPN built into the Linux kernel. Compared with older VPN stacks it
has a tiny configuration surface: each side has a key pair, and each peer is a few lines in one file. That
simplicity is why it has become the default choice for remote access to a private LAN or for linking two sites.

This guide builds a server that accepts remote clients and routes them to a private network, then covers the
firewall rules from our UFW guide or
nftables guide that keep it safe, and day-two tasks such as adding and revoking peers.

How the WireGuard Topology Fits Together

A WireGuard server is just a Linux host with a wg0 interface. Each client gets a tunnel address inside a private VPN subnet (here 10.8.0.0/24), and the server forwards traffic between that subnet and the LAN behind it. There is no client/server distinction in the protocol — the server is simply the peer with a stable public address and a listening UDP port.

architecturewireguard-topology.txt
  [Laptop 10.8.0.2] --UDP 51820--> [Server wg0 10.8.0.1] ----> [LAN 192.168.10.0/24]
  [Phone  10.8.0.3] --UDP 51820-->        |
                                      NAT / forwarding
WireGuard client, server and LAN topology
One tunnel subnet bridging clients and the LAN

Install WireGuard and Enable IP Forwarding

Current Debian, Ubuntu, AlmaLinux and Rocky releases ship WireGuard in the kernel; you only install the tools. The server must also forward packets, otherwise clients can reach the server but nothing behind it.

bashinstall
# Debian / Ubuntu
sudo apt install -y wireguard
# AlmaLinux / Rocky (EPEL)
sudo dnf install -y wireguard-tools
bash/etc/sysctl.d/99-wireguard.conf
net.ipv4.ip_forward = 1
bashapply
sudo sysctl --system
Installing WireGuard and enabling forwarding
Tools plus kernel forwarding

Generate the Server and Client Key Pairs

Each peer has a private key that never leaves its host and a public key you share with the other side. Set a restrictive umask so the private key file is not world-readable.

bashserver keys
umask 077
wg genkey | sudo tee /etc/wireguard/server.key | wg pubkey | sudo tee /etc/wireguard/server.pub
bashclient keys (run on the client)
umask 077
wg genkey | tee client.key | wg pubkey > client.pub
WarningNever paste a private key into a ticket, chat or repository. Only .pub files are shared.
WireGuard key pair generation
Private keys stay local, public keys are exchanged

Write the Server Configuration

The [Interface] block describes the server itself; each [Peer] block admits one client and pins the tunnel address it may use. PostUp and PostDown add and remove the NAT rule so the config stays self-cleaning. Replace eth0 with your real uplink interface.

ini/etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of server.key>
PostUp   = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# laptop
PublicKey = <contents of client.pub>
AllowedIPs = 10.8.0.2/32
Tipchmod 600 /etc/wireguard/wg0.conf — the file embeds the server private key.
Server wg0.conf structure
Interface, NAT hooks and one peer

Open the Firewall for the WireGuard Port

WireGuard is silent to unauthenticated packets, so exposing one UDP port is low risk. Allow only that port from the internet and let the tunnel interface forward.

bashufw
sudo ufw allow 51820/udp
sudo ufw route allow in on wg0 out on eth0
Firewall rule for UDP 51820
One UDP port, nothing else

Create the Client Configuration

The client’s AllowedIPs decides what travels through the tunnel. 0.0.0.0/0 sends everything (full tunnel); listing only the VPN subnet and LAN gives a split tunnel that leaves normal browsing on the local connection. Add PersistentKeepalive when the client sits behind NAT.

iniclient wg0.conf
[Interface]
Address = 10.8.0.2/24
PrivateKey = <contents of client.key>
DNS = 192.168.10.1

[Peer]
PublicKey = <contents of server.pub>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.8.0.0/24, 192.168.10.0/24
PersistentKeepalive = 25
Client config choices
Full tunnel versus split tunnel

Bring the Tunnel Up and Verify the Handshake

Enable the interface on boot with the systemd template unit, then confirm a handshake actually occurred — an interface that is up but shows no handshake means keys, endpoint or firewall are wrong.

bashserver
sudo systemctl enable --now wg-quick@wg0
sudo wg show
bashclient
sudo wg-quick up wg0
ping -c 3 10.8.0.1
ping -c 3 192.168.10.1
Notelatest handshake in wg show should be within the last two minutes while traffic flows.
Verifying a WireGuard handshake
Look for a recent handshake, not just an up interface

Add and Revoke Peers Without Downtime

Adding a client means a new [Peer] block with a unique tunnel address. Reload the running interface with wg syncconf so existing sessions are not dropped. To revoke, delete the block and sync again; the peer’s key is immediately useless.

bashreload
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'
Adding and revoking peers
Peer lifecycle without restarting the tunnel

Quick Reference

  • UDP 51820 open; IP forwarding on; one /32 per peer
  • Private keys mode 600; rotate a peer by replacing its key pair

Related tutorials

Diagrams are original illustrations by Gnome IT Solutions. Tutorial text © Gnome IT Solutions.