WireGuard gives Ubuntu servers a lightweight way to create encrypted tunnels without a hosted control plane or a bulky VPN daemon. Ubuntu ships the kernel module in current kernels, so setup usually means installing the wireguard package, generating peer keys, and letting wg-quick manage the wg0 interface through systemd.
The commands target Ubuntu 26.04 LTS, 24.04 LTS, and 22.04 LTS. The worked example creates one IPv4 WireGuard server at 10.8.0.1, one client at 10.8.0.2, and optional gateway routing so the client can send traffic through the Ubuntu server.
Install WireGuard on Ubuntu
Ubuntu’s WireGuard package path is simple because current Ubuntu kernels already include the WireGuard kernel module. The wireguard metapackage installs the user tools through wireguard-tools, including wg, wg-quick, and the wg-quick@.service systemd template.
The upstream WireGuard installation page lists Ubuntu as a package-based module and tools install. The
wireguard-linuxGit tree is kernel development source, not a normal “latest stable” Ubuntu installer. Use APT for standard Ubuntu systems; reserve source or DKMS work for custom kernels that you can validate and maintain separately.
Check the WireGuard Package Candidate
Refresh package metadata, then confirm that Ubuntu can see both the metapackage and the tools package:
sudo apt update
apt-cache policy wireguard wireguard-tools
Ubuntu 26.04 currently reports a newer tool package than Ubuntu 24.04 and 22.04, but all three releases provide the same package roles:
wireguard:
Installed: (none)
Candidate: 1.0.20250521-1ubuntu1
Version table:
1.0.20250521-1ubuntu1 500
500 http://archive.ubuntu.com/ubuntu resolute/universe amd64 Packages
wireguard-tools:
Installed: (none)
Candidate: 1.0.20250521-1ubuntu1
Version table:
1.0.20250521-1ubuntu1 500
500 http://archive.ubuntu.com/ubuntu resolute/main amd64 Packages
If wireguard has no candidate, the Universe component is probably disabled on that system. Enable Ubuntu Universe repositories, refresh APT again, and repeat the package check.
Install the WireGuard Package
Install the metapackage from Ubuntu’s repositories:
sudo apt install wireguard -y
These commands use
sudofor package, service, and network changes that need root privileges. If your account is not ready for administrative commands, add a sudo user on Ubuntu before continuing.
Verify WireGuard Tools and Kernel Module
Check the installed tools and confirm that the kernel module is available:
wg --version
command -v wg-quick
sudo modprobe wireguard
modinfo wireguard | head -n 8
Example output from Ubuntu 26.04 includes the WireGuard tools version and the in-tree kernel module path:
wireguard-tools v1.0.20250521 - https://git.zx2c4.com/wireguard-tools/ /usr/bin/wg-quick filename: /lib/modules/7.0.0-15-generic/kernel/drivers/net/wireguard/wireguard.ko.zst version: 1.0.0 description: WireGuard secure network tunnel license: GPL v2
Ubuntu 24.04 and 22.04 can show wireguard-tools v1.0.20210914. That tools version does not mean the VPN protocol is outdated; the kernel module and tools are packaged for each Ubuntu release branch.
Plan WireGuard Server and Client Settings
WireGuard does not allocate addresses with DHCP. Choose a small private subnet that does not overlap your LAN, cloud VPC, Docker networks, or another VPN. The example values work for a single-client setup and can be expanded with more peers later.
| Setting | Example Value | Purpose |
|---|---|---|
| Server interface | wg0 | Systemd instance and tunnel interface name |
| Server tunnel address | 10.8.0.1/24 | Gateway address inside the WireGuard subnet |
| Client tunnel address | 10.8.0.2/32 | First client address allowed by the server peer entry |
| UDP listener | 51820/udp | Common example port for incoming WireGuard handshakes |
| Server public endpoint | vpn.example.com:51820 | DNS name or public IP address clients use to reach the server |
| Full-tunnel client route | 0.0.0.0/0 | Sends IPv4 client traffic through the VPN gateway |
AllowedIPs is the most important setting to understand before editing the files. On the server, each peer’s AllowedIPs should be narrow, such as 10.8.0.2/32, so one peer cannot claim another peer’s address. On the client, AllowedIPs controls what traffic enters the tunnel. Use 0.0.0.0/0 for a full IPv4 tunnel, or use narrower networks such as 10.8.0.0/24, 192.168.1.0/24 for a split tunnel.
Find the server’s default outbound network interface before you write NAT rules. The interface name is the value after dev:
ip route show default
default via 192.0.2.1 dev eth0 proto dhcp src 192.0.2.10 metric 100
The example output uses eth0. Your server may use ens3, enp1s0, wlan0, or another name.
Configure the WireGuard Server on Ubuntu
The server configuration owns the listening port, the server private key, the server tunnel address, and a peer entry for each client. The full-tunnel example also enables IPv4 forwarding and adds a NAT rule when wg0 starts.
Enable IPv4 Forwarding for Gateway Mode
Enable forwarding when clients need to reach the internet or another network through the Ubuntu server. Do not create this sysctl file if the tunnel is only for reaching the server itself.
printf 'net.ipv4.ip_forward = 1\n' | sudo tee /etc/sysctl.d/99-wireguard-forwarding.conf > /dev/null
sudo sysctl -p /etc/sysctl.d/99-wireguard-forwarding.conf
net.ipv4.ip_forward = 1
For IPv6 tunnels, plan IPv6 addressing, forwarding, firewalling, and provider routing separately. Do not add ::/0 to clients until the server has a working IPv6 forwarding and firewall design.
Generate Server and Client Keys
Create the WireGuard directory with restrictive permissions, then generate one key pair for the server and one key pair for the first client:
sudo install -d -m 700 /etc/wireguard /etc/wireguard/clients
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key'
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/clients/client1_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/clients/client1_private.key > /etc/wireguard/clients/client1_public.key'
Print only the public keys when you need to compare or copy values:
sudo cat /etc/wireguard/server_public.key
sudo cat /etc/wireguard/clients/client1_public.key
Private keys authenticate the peer that owns them. Do not paste private keys into tickets, chat, screenshots, or public logs. If a client is managed by another person, have that client generate its own private key and send you only the client public key.
Create the Server wg0 Configuration
Set public_interface to the outbound interface from the earlier route check, then create /etc/wireguard/wg0.conf:
public_interface="eth0"
server_private=$(sudo cat /etc/wireguard/server_private.key)
client_public=$(sudo cat /etc/wireguard/clients/client1_public.key)
sudo tee /etc/wireguard/wg0.conf > /dev/null <<EOF
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = ${server_private}
SaveConfig = false
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o ${public_interface} -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o ${public_interface} -j MASQUERADE
[Peer]
PublicKey = ${client_public}
AllowedIPs = 10.8.0.2/32
EOF
sudo chmod 600 /etc/wireguard/wg0.conf
sudo chown root:root /etc/wireguard/wg0.conf
The PostUp and PostDown lines add and remove the IPv4 masquerade rule with the interface. Remove those two lines if clients only need to reach 10.8.0.1 on the VPN server and do not need routed traffic.
Start and Enable the WireGuard Server
Start the wg0 interface now and enable it at boot:
sudo systemctl enable --now wg-quick@wg0.service
Verify the systemd state and WireGuard interface:
systemctl is-active wg-quick@wg0.service
sudo wg show wg0
ip -brief address show wg0
Example output with public keys replaced by labels looks like this:
active interface: wg0 public key: SERVER_PUBLIC_KEY private key: (hidden) listening port: 51820 wg0 UNKNOWN 10.8.0.1/24
The peer will not show a recent handshake until the client imports its configuration and sends traffic.
Allow WireGuard Through UFW
Open UFW only after the local wg0 listener is active. Check UFW status first, especially on remote servers where a missing SSH rule can lock out the current session:
sudo ufw status verbose
If UFW reports Status: active, allow the WireGuard UDP listener. For a broader firewall baseline, use the Ubuntu UFW firewall guide. The Ubuntu SSH setup guide covers listener and firewall checks for remote access.
sudo ufw allow 51820/udp
For gateway mode with UFW active, also allow routed packets from wg0 to the server’s outbound interface. Replace eth0 with the same interface used in wg0.conf:
sudo ufw route allow in on wg0 out on eth0
Cloud firewalls, router port forwards, and hosting-provider security groups are separate from UFW. Open or forward the same UDP port in that control plane when the server sits behind one.
If you prefer native rules instead of UFW, configure nftables on Ubuntu and keep one firewall manager responsible for the same host ruleset.
Create a WireGuard Client Configuration
The client configuration needs the client private key, the client tunnel address, the server public key, and the server endpoint. Set server_endpoint to a DNS name or public IP address that reaches the Ubuntu server.
server_endpoint="vpn.example.com"
server_public=$(sudo cat /etc/wireguard/server_public.key)
client_private=$(sudo cat /etc/wireguard/clients/client1_private.key)
sudo tee /etc/wireguard/clients/client1.conf > /dev/null <<EOF
[Interface]
PrivateKey = ${client_private}
Address = 10.8.0.2/32
DNS = 1.1.1.1
[Peer]
PublicKey = ${server_public}
Endpoint = ${server_endpoint}:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF
sudo chmod 600 /etc/wireguard/clients/client1.conf
For a split tunnel, replace AllowedIPs = 0.0.0.0/0 with the VPN and private networks that should use the tunnel, such as AllowedIPs = 10.8.0.0/24, 192.168.1.0/24. The PersistentKeepalive = 25 setting is useful for laptops, phones, and clients behind NAT because it keeps the mapping open enough for the server to reply.
The client configuration contains the client private key. Anyone who receives that file can authenticate as that peer until you remove or replace the peer public key on the server.
Copy the client file to the client system through a secure channel, or print it for import into a WireGuard app only when your terminal is private:
sudo cat /etc/wireguard/clients/client1.conf
Optional QR Code for Mobile Clients
Phone clients can import a WireGuard profile from a QR code. Install qrencode only on the system where you want to display the code:
The QR code contains the client private key. Generate it only in a private terminal, avoid screen sharing while it is visible, and close the terminal when the import finishes.
sudo apt install qrencode -y
sudo qrencode -t ansiutf8 < /etc/wireguard/clients/client1.conf
Scan the terminal QR code from the official WireGuard mobile app.
Connect an Ubuntu WireGuard Client
On an Ubuntu client, install WireGuard the same way as the server:
sudo apt update
sudo apt install wireguard -y
Client profiles that use a DNS = line need a resolvconf-compatible command. Ubuntu 26.04 and 24.04 usually provide that compatibility through systemd-resolved. Check the client before starting the interface:
command -v resolvconf
/usr/sbin/resolvconf
If an Ubuntu 22.04 client prints no path, install openresolv before starting the interface. On Ubuntu 26.04 and 24.04, keep systemd-resolved installed because it provides /usr/sbin/resolvconf on those releases.
sudo apt install openresolv
Save the client configuration as /etc/wireguard/wg0.conf, then lock down the file:
sudo install -d -m 700 /etc/wireguard
sudo nano /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/wg0.conf
sudo chown root:root /etc/wireguard/wg0.conf
Start the client tunnel and enable it at boot:
sudo systemctl enable --now wg-quick@wg0.service
Check the client interface and handshake state:
sudo wg show wg0
ping -c 4 10.8.0.1
After the client sends traffic, wg show should include a recent handshake and transfer counters. Example output with public keys replaced by labels looks like this:
interface: wg0 public key: CLIENT_PUBLIC_KEY private key: (hidden) peer: SERVER_PUBLIC_KEY endpoint: vpn.example.com:51820 allowed ips: 0.0.0.0/0 latest handshake: 2 seconds ago transfer: 476 B received, 564 B sent
A working tunnel shows a recent handshake, transfer counters, and successful replies from 10.8.0.1. For a full-tunnel setup, compare the client’s public IP address before and after connection to confirm traffic exits through the server.
Manage WireGuard on Ubuntu
Use systemd for normal interface lifecycle work and wg for live tunnel state:
systemctl status wg-quick@wg0.service --no-pager
sudo wg show wg0
Restart the interface after changing addresses, NAT commands, DNS lines, or firewall-dependent settings:
sudo systemctl restart wg-quick@wg0.service
If you only add, remove, or edit peer entries in wg0.conf, reload the systemd instance and recheck the peer list:
sudo systemctl reload wg-quick@wg0.service
sudo wg show wg0
Update WireGuard Packages
WireGuard updates through Ubuntu’s normal APT workflow. Upgrade the installed WireGuard packages without pulling in unrelated new packages:
sudo apt update
sudo apt install --only-upgrade wireguard wireguard-tools
Kernel updates can also update the in-tree WireGuard module. Reboot when Ubuntu installs a new kernel and the server’s maintenance window allows it.
Optional helper packages such as qrencode and openresolv update through APT as well when they are installed.
Remove WireGuard from Ubuntu
Stop and disable the wg0 interface before removing packages:
sudo systemctl disable --now wg-quick@wg0.service
Remove the WireGuard packages:
sudo apt remove wireguard wireguard-tools
If you installed qrencode only for mobile profile import, remove it separately:
sudo apt remove qrencode
If you installed openresolv only for WireGuard client DNS handling, remove it only after confirming no other VPN, network profile, or resolver workflow still needs /usr/sbin/resolvconf:
sudo apt remove openresolv
APT may also offer automatically installed dependencies such as libqrencode4. Review the proposed list before accepting the cleanup:
sudo apt autoremove
Remove UFW rules only if you added them for this WireGuard interface. Replace eth0 with your actual outbound interface:
sudo ufw delete allow 51820/udp
sudo ufw route delete allow in on wg0 out on eth0
Review configuration and key files before deleting them:
sudo find /etc/wireguard -maxdepth 2 -type f -print
Deleting
/etc/wireguardpermanently removes server keys, client keys, peer configs, and exported client profiles stored there. Back up anything you may need to reconnect existing peers.
sudo rm -rf /etc/wireguard
If the server used IPv4 forwarding only for WireGuard, remove the WireGuard forwarding sysctl file and reapply the remaining sysctl configuration:
sudo rm -f /etc/sysctl.d/99-wireguard-forwarding.conf
sudo sysctl --system
Leave forwarding enabled if the system still routes Docker networks, virtual machines, Kubernetes traffic, another VPN, or a router workload.
Troubleshoot WireGuard on Ubuntu
WireGuard Service Fails to Start
Start with the service status and recent unit logs. Most startup failures come from a bad key, a typo in wg0.conf, an already-used address, or a failing PostUp command.
systemctl status wg-quick@wg0.service --no-pager
sudo journalctl -u wg-quick@wg0.service --no-pager -n 50
If the log names iptables, recheck the outbound interface in the PostUp line. If the log names an address conflict, confirm that no old interface is still present:
ip -brief address show wg0
If wg0 is still present and should not be running, bring it down before retrying the service start:
sudo wg-quick down wg0
After fixing wg0.conf, restart the interface and recheck the service state:
sudo systemctl restart wg-quick@wg0.service
systemctl is-active wg-quick@wg0.service
No Latest Handshake Appears
A missing handshake usually means the client cannot reach the server’s UDP listener or one side has the wrong public key. Check the server first:
sudo wg show wg0
sudo ufw status verbose
Confirm that the client Endpoint points at the server’s reachable public IP address or DNS name, that UDP 51820 is open in UFW and any cloud firewall, and that a router forwards the same UDP port to the Ubuntu server when the server is behind NAT.
Client Connects but Cannot Reach the Internet
A successful handshake with no routed traffic usually points to forwarding, NAT, UFW route policy, or client AllowedIPs. Check the server’s forwarding state and default interface:
sysctl net.ipv4.ip_forward
ip route show default
sudo ufw status verbose
For the full-tunnel example, net.ipv4.ip_forward should be 1, the PostUp interface should match the default route interface, and UFW should allow routed traffic from wg0 to that interface. The client should use AllowedIPs = 0.0.0.0/0 for IPv4 full-tunnel routing.
DNS Fails After the Tunnel Connects
If the client service fails with resolvconf: command not found, install the release-specific provider and start the interface again. On Ubuntu 22.04, use openresolv:
sudo apt install openresolv
sudo systemctl restart wg-quick@wg0.service
On Ubuntu 26.04 and 24.04, systemd-resolved provides /usr/sbin/resolvconf:
sudo apt install systemd-resolved
sudo systemctl restart wg-quick@wg0.service
If IP addresses work but hostnames fail, inspect the DNS line in the client configuration and the resolver state on the client:
sudo grep '^DNS' /etc/wireguard/wg0.conf
resolvectl status
Use a resolver that is reachable through the tunnel, such as an internal DNS server for private networks or a public resolver for a normal full-tunnel setup. Restart the client interface after changing the DNS line.
Conclusion
WireGuard is running on Ubuntu with APT-managed tools, locked-down keys, a systemd-managed wg0 interface, and a first client profile ready to import. Keep peer addresses narrow on the server, open only the UDP path you need, and use Tailscale on Ubuntu when you would rather use a managed WireGuard-based overlay than maintain your own endpoint and peer files.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>