WireGuard Core Concepts
WireGuard is an L3 VPN protocol integrated into the Linux kernel. Each "peer" is identified by a public-key/private-key pair, and packets are transmitted in an encrypted format over UDP port 51820. As stated in the Ubuntu Server documentation, "no additional firewall rules are required on hosts inside the networks" (Ubuntu, site-to-site documentation). This means the WireGuard interface only needs to be configured on the gateways. This kernel-level module offers low latency and high throughput; furthermore, thanks to "cryptokey routing," packets are added directly to the kernel routing table.
Key Management and Security
Keys can be generated using the wg genkey and wg pubkey commands:
# Generate private key
wg genkey | tee /etc/wireguard/privkey | wg pubkey > /etc/wireguard/pubkey
These commands are provided by the wireguard package; it must be installed from the Ubuntu 22.04 LTS repositories. The private key should never be exported; it is kept only in the /etc/wireguard/ directory of the relevant gateway, and file permissions should be set to 600. The public key is added to the Peer section of the remote site's configuration.
Security note: Generating and backing up the private key once simplifies the "key rotation" procedure for future reconfigurations. The Ubuntu documentation suggests creating a new private key for key rotation and restarting the existing tunnel using
wg-quick down→wg-quick up.
Route Planning and Subnet Selection
In a site-to-site VPN, the LANs of two offices must be connected in an isolated manner. Choosing non-overlapping subnets is critical; otherwise, a "routing loop" will occur. For example:
- Site A LAN:
10.10.0.0/16 - Site B LAN:
10.20.0.0/16
Routing between these two networks is achieved by defining the other side's LAN in the AllowedIPs field of each gateway. In Ubuntu documentation, "cryptokey routing" is automatically added with a line like AllowedIPs = 10.20.0.0/16.
Edge case: If two offices have the same subnet (e.g.,
10.0.0.0/16), you must use NAT-over-VPN; in this case,iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADEis added viaPostUpandPostDownscripts. However, since this can lead to performance loss, non-overlapping subnets should be preferred whenever possible.
Ubuntu Server Installation Steps
The basic steps for installing WireGuard on Ubuntu Server 22.04 LTS are as follows:
# 1. install packages
sudo apt update
sudo apt install wireguard
# 2. create configuration directory
sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
# 3. generate private and public keys
wg genkey | sudo tee /etc/wireguard/siteA.key | wg pubkey | sudo tee /etc/wireguard/siteA.pub
The generated keys are saved to siteA.key and siteA.pub files. Subsequently, the wg0.conf file follows this example structure:
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <siteA_private_key>
[Peer]
PublicKey = <siteB_public_key>
Endpoint = 203.0.113.2:51820
AllowedIPs = 10.20.0.0/16
PersistentKeepalive = 25
<siteA_private_key> and <siteB_public_key> must be replaced with actual values. After creating the file:
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0.service
These commands activate the interface and start it automatically at system boot. The statement "no changes needed on individual hosts" in the official Ubuntu documentation confirms that this step only needs to be performed on the gateways.
Example Scenario: Site-to-Site Connection Between Two Offices
Scenario:
- Site A: Istanbul office,
10.10.0.0/16, external IP198.51.100.10. - Site B: Ankara office,
10.20.0.0/16, external IP203.0.113.2.
Both sites install WireGuard on a VM running Ubuntu 22.04 LTS. The following steps show how to establish the two-way tunnel.
# Site A (Istanbul) – get public key from Site B
sudo wg set wg0 peer <siteB_pub> allowed-ips 10.20.0.0/16 endpoint 203.0.113.2:51820 persistent-keepalive 25
# Site B (Ankara) – perform the same process in reverse
sudo wg set wg0 peer <siteA_pub> allowed-ips 10.10.0.0/16 endpoint 198.51.100.10:51820 persistent-keepalive 25
To verify the connection:
sudo wg show
The output shows the latest handshake time and the amount of data transferred for each peer. The warning in the Ubuntu documentation that "no changes needed on individual hosts" emphasizes that machines within the LAN do not require additional configuration; communication is simply checked with tests like ping 10.20.0.1.
Verification and Monitoring
The operational status of WireGuard can be monitored with the wg command:
sudo wg show wg0
This lists the received and sent byte values for each peer. Additionally, examining logs via the systemd journal is useful for detecting potential handshake errors:
journalctl -u wg-quick@wg0 -f
Ubuntu's official guide states that "no additional firewall rules" are required; however, allowing only UDP 51820 via ufw or iptables is a good security layer:
sudo ufw allow 51820/udp
This step prevents external attackers from establishing a tunnel over a different port.
Rollback and Maintenance
It is recommended to take a copy of the existing file before making configuration changes:
sudo cp /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf.bak
When a new key is generated or a subnet is changed, the following steps are followed:
- wg-quick down wg0 – close the active tunnel.
- Update the wg0.conf file.
- wg-quick up wg0 – load the new configuration.
This process is defined as the "rollback" procedure in Ubuntu documentation. If the new configuration creates an unexpected routing problem, restoring the backup file and repeating the same steps resolves the issue.
sudo cp /etc/wireguard/wg0.conf.bak /etc/wireguard/wg0.conf
sudo wg-quick down wg0 && sudo wg-quick up wg0
Network Topology – Mermaid Diagram
Key Rotation Strategy
Key rotation keeps a WireGuard tunnel resilient against long-term key compromise.
The rotation process is deliberately simple: generate a new private key, stop the tunnel, replace the key in the configuration, and bring the tunnel back up.
The following sequence illustrates the minimal steps required on a Ubuntu 22.04 gateway:
# 1. Generate a fresh private key
sudo wg genkey | sudo tee /etc/wireguard/new.key
# 2. Derive the corresponding public key
sudo cat /etc/wireguard/new.key | wg pubkey | sudo tee /etc/wireguard/new.pub
# 3. Preserve the current configuration
sudo cp /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf.bak
# 4. Update the private key in the interface section
sudo sed -i 's|PrivateKey =.*|PrivateKey = '"$(cat /etc/wireguard/new.key)"'|' /etc/wireguard/wg0.conf
# 5. Restart the tunnel
sudo wg-quick down wg0
sudo wg-quick up wg0
If the tunnel fails to reconnect, the original key is restored with:
sudo cp /etc/wireguard/wg0.conf.bak /etc/wireguard/wg0.conf
sudo wg-quick down wg0 && sudo wg-quick up wg0
Because the public key changes, each peer must be notified and updated with the new public key via wg set or by exchanging the updated wg0.conf file. The rotation procedure is idempotent: running the steps multiple times does not introduce side effects, and the rollback path is always available through the backup file.
Firewall and NAT Considerations
WireGuard itself operates over UDP port 51820; however, explicit firewall rules are recommended to restrict inbound traffic and prevent accidental exposure.
On Ubuntu, the uncomplicated UFW utility can be leveraged:
sudo ufw allow 51820/udp
sudo ufw enable
When both sites share identical internal subnets, NAT-over-VPN becomes necessary to avoid routing ambiguity. This is handled by adding PostUp and PostDown directives to the interface configuration:
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <private_key>
PostUp = iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o %i -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.10.0.0/24 -o eth0 -j MASQUERADE
The PostUp rule ensures that traffic originating from the VPN subnet is masqueraded to the public interface (eth0), while PostDown cleanly removes the rule when the tunnel is torn down. This setup preserves packet integrity on the LAN side and eliminates the need for per-host configuration changes, keeping the “no additional firewall rules” principle intact for internal hosts.
Performance Monitoring and Alerting
Operational visibility is critical for a site-to-site VPN. WireGuard exposes real-time statistics via the wg command, which can be scraped by monitoring tools. A lightweight Prometheus exporter for WireGuard, such as wg-exporter, aggregates metrics like wireguard_peer_handshakes, wireguard_peer_bytes_received, and wireguard_peer_bytes_sent.
A typical Prometheus scrape configuration looks like:
scrape_configs:
- job_name: wireguard
static_configs:
- targets: ['localhost:9798']
On the alerting side, Grafana dashboards can display latency, packet loss, and handshake frequency. Simple alert rules are:
groups:
- name: wireguard
rules:
- alert: WireGuardDown
expr: node_wg_interface_up{device="wg0"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "WireGuard interface wg0 is down"
- alert: HandshakeStale
expr: time() - node_wg_peer_latest_handshake{device="wg0"} > 600
for: 5m
labels:
severity: warning
annotations:
summary: "No handshake with peer in the last 10 minutes"
These alerts trigger when the tunnel is inactive or when peers fail to perform a handshake within a defined window. Coupling the exporter with Grafana provides a single pane of glass for both health and performance, allowing administrators to react before user-visible degradation occurs.
Conclusion
This guide demonstrated step-by-step how to perform key generation, configuration, and route design for a WireGuard-based site-to-site VPN in an Ubuntu Server 22.04 LTS environment. Through correct subnet selection, public/private key management, and AllowedIPs definitions, a secure, low-latency connection can be established between two offices. The rollback procedure simplifies reverting configuration changes, while monitoring commands allow you to check the health of the tunnel in real-time.
Next step: If you have a high availability (HA) requirement in your network, try creating a "redundant tunnel" with two separate WireGuard gateways; this ensures that the connection continues uninterrupted in the event of a single endpoint failure.
Top comments (0)