WireGuard is a VPN built into the Linux kernel, with a configuration reduced to a few text files: each device has a key pair, lists the peers it talks to and declares which addresses it can reach through each one. The protocol makes no distinction between server and client: the "server" is simply the peer with a reachable address. The two most common scenarios are a device's remote access to the lab network and a permanent link between two sites; both are described here for Debian 12 and 13.
How WireGuard thinks
The central concept is AllowedIPs, which does two jobs at once for each peer. Outbound, it is a routing table: a packet destined to an address in the list is encrypted and sent to that peer. Inbound, it is a filter: a decrypted packet whose source address does not belong to the list of the peer that sent it is dropped. The list must therefore be written precisely, not as a simple set of networks to reach. In addition, WireGuard does not answer packets that do not come from an authenticated peer: toward the Internet you only need to expose a single UDP port, which reveals no service.
Installation and keys
WireGuard support is in the Debian 12 and 13 kernel, so only the tools package is needed. Each device generates its own key pair on the device itself: the private key must never leave it.
apt install wireguard-tools
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pubumask 077 makes the files readable only by the owner. The contents of server.key go in the PrivateKey field of the configuration; server.pub is the only key to share with the other peers. In the examples below, values in angle brackets are placeholders.
Important
A private key is never pasted into chat, tickets, repositories or documentation, and is never shared between devices: compromising a shared key forces you to replace it everywhere.
Remote access
In the example the lab network is 192.0.2.0/24, the tunnel uses 203.0.113.0/24 and the laptop has the address 203.0.113.2. The server is reachable as vpn.example.com.
[Interface]
Address = 203.0.113.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
# example laptop
[Peer]
PublicKey = <PC_PUBLIC_KEY>
AllowedIPs = 203.0.113.2/32[Interface]
Address = 203.0.113.2/32
PrivateKey = <PC_PRIVATE_KEY>
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820
AllowedIPs = 203.0.113.1/32, 192.0.2.0/24
PersistentKeepalive = 25- On the server,
AllowedIPs = 203.0.113.2/32is the only source address accepted from that peer: a compromised device cannot impersonate another. - On the client,
AllowedIPsdetermines what goes into the tunnel: here the server's address and the lab network (split tunnel). With0.0.0.0/0all traffic would pass through andwg-quickwould also set the default route. PersistentKeepalive = 25sends a packet every 25 seconds to keep the NAT mapping open. It is needed by a peer that sits behind a NAT and must be able to receive traffic; by default it is off.- UDP port 51820 is only a convention: you choose it with
ListenPort.
Note
The name in Endpoint is resolved when the interface is configured, not on every packet. If the server's public address changes (dynamic IP), the client must restart the tunnel or have a re-resolution mechanism.
Starting, forwarding and verifying
For a manual test use wg-quick up wg0; the configuration file must be readable only by root, otherwise wg-quick warns about it.
chmod 600 /etc/wireguard/wg0.conf
wg-quick up wg0For automatic startup, bring the interface down and enable the systemd unit, which runs the same wg-quick up at every boot. The two methods must not be combined on the same interface.
wg-quick down wg0
systemctl enable --now wg-quick@wg0For the server to forward packets between the tunnel and the local network, IPv4 forwarding is required.
net.ipv4.ip_forward = 1sysctl --systemDevices on the local network must know how to reply to the tunnel addresses: either the WireGuard server is their default gateway, or a static route to 203.0.113.0/24 is added on the router. Alternatively the server can masquerade (NAT) the tunnel traffic, at the cost of losing the client's real address in the logs. Forwarding and NAT rules belong to the host's firewall and should be limited to what is necessary; on the perimeter firewall you forward only the chosen UDP port.
wg showinterface: wg0
public key: <SERVER_PUBLIC_KEY>
listening port: 51820
peer: <PC_PUBLIC_KEY>
endpoint: 198.51.100.77:41234
allowed ips: 203.0.113.2/32
latest handshake: 12 seconds agoIf latest handshake does not appear, the peer has never completed the exchange: check the public keys, the reachability of the UDP port and the Endpoint.
Site-to-site VPN
Two sites are linked with the same mechanism, giving the remote networks as AllowedIPs. In the example site A has a stable public address and site B sits behind a NAT. Site A's network is 192.0.2.0/24, site B's is 198.51.100.0/24, and the tunnel is 203.0.113.0/30.
[Interface]
Address = 203.0.113.1/30
ListenPort = 51820
PrivateKey = <SITE_A_PRIVATE_KEY>
[Peer]
PublicKey = <SITE_B_PUBLIC_KEY>
AllowedIPs = 203.0.113.2/32, 198.51.100.0/24[Interface]
Address = 203.0.113.2/30
PrivateKey = <SITE_B_PRIVATE_KEY>
[Peer]
PublicKey = <SITE_A_PUBLIC_KEY>
Endpoint = site-a.example.com:51820
AllowedIPs = 203.0.113.1/32, 192.0.2.0/24
PersistentKeepalive = 25Site B initiates the connection; site A learns its address from the packets it receives, which is why it does not need an Endpoint. For the link to work:
- the two local networks have different, non-overlapping subnets;
- each peer's
AllowedIPsincludes the other site's network; - both gateways enable
net.ipv4.ip_forward; - the hosts at each site know the route to the remote network (default gateway or a static route on the router).
Warning
A tunnel between sites does not mean mutual trust: without filters every device at one site reaches all those at the other. Allow only the flows that are needed. The tunnel is, for example, the right channel for replicating backups to the remote site (see 3-2-1 backup strategy with Proxmox Backup Server).
Best practices
- One key per device. Each device has a dedicated pair and a
[Peer]block with a comment identifying it. - Revocation. Delete the device's block from the file and apply the change without interrupting the other tunnels with
wg syncconf wg0 <(wg-quick strip wg0). - Rotation. Generate a new pair on the device, update the public key on the counterpart and remove the old one; do it at every device replacement and at any suspicion of compromise. A preshared key per pair of peers (
wg genpsk,PresharedKeyfield) adds an extra symmetric layer. - Least privilege. VPN clients reach only the destinations they need, with rules on the
wg0interface and, ideally, in a dedicated segment (see Segmenting the network with VLANs). - Minimal exposure. Only the UDP port stays open toward the Internet; SSH and administration interfaces are reached only through the tunnel (see the Debian SSH hardening checklist).
- Monitoring.
wg showand thelatest handshakevalue tell you whether a peer is active. Configuration files have600permissions and their copies should be encrypted, because they contain the private key.