How to Install and Configure a WireGuard VPN Server and Client on Ubuntu, Debian, and Linux Mint

Written by

in

WireGuard is a lightweight, high-performance VPN protocol designed to provide secure network tunnels with a relatively simple configuration model. It is integrated into modern Linux kernels and can be managed through the wg and wg-quick utilities.

This tutorial explains how to install and configure a WireGuard VPN server and a Linux client on:

  • Ubuntu 24.04 LTS
  • Ubuntu 22.04 LTS
  • Debian 12 or later
  • Linux Mint 21 or later

The completed configuration will allow the client to route all IPv4 internet traffic through the WireGuard VPN server.

Network Configuration Used in This Tutorial

The following example values are used throughout this guide:

SettingExample value
WireGuard interfacewg0
Server VPN address10.8.0.1/24
Client VPN address10.8.0.2/24
WireGuard UDP port51820
Server public IP203.0.113.10
Server internet interfaceeth0
VPN network10.8.0.0/24

Replace the example server IP address and network interface with the actual values from your environment.


Prerequisites

Before starting, make sure you have:

  • A VPS or server running Ubuntu, Debian, or Linux Mint.
  • Root access or a user account with sudo privileges.
  • SSH access to the VPN server.
  • A public IPv4 address or a hostname pointing to the server.
  • UDP port 51820 allowed by the hosting provider or cloud firewall.
  • A Linux client on which WireGuard can be installed.

This guide assumes that the server has direct internet access and will act as the default gateway for the VPN client.


Part 1: Configure the WireGuard Server

Step 1: Update the Server

Connect to the server through SSH:

ssh username@SERVER_PUBLIC_IP

Update the package index:

sudo apt update

Optionally install available package updates:

sudo apt upgrade -y

The original command:

sudo apt updatesudo apt install wireguard

is invalid because it combines two commands without a separator. Each command must be placed on a separate line or joined with &&.


Step 2: Install WireGuard

Install the WireGuard userspace tools:

sudo apt install wireguard -y

Verify that the command is available:

wg --version

You can also confirm that the kernel supports WireGuard:

sudo modprobe wireguard

Check whether the module was loaded:

lsmod | grep wireguard

On modern Ubuntu and Debian systems, WireGuard support is normally included in the kernel, while the wireguard package provides the administration utilities and related components.


Step 3: Identify the Server’s Internet Interface

The WireGuard server must perform Network Address Translation on its external network interface.

Run:

ip route show default

Example output:

default via 192.0.2.1 dev eth0 proto dhcp src 203.0.113.10

In this example, the external interface is:

eth0

Depending on the server, the interface may instead be named:

ens3
enp1s0
ens18
venet0

You can extract the interface name automatically with:

ip route show default | awk '/default/ {print $5; exit}'

Record the result because it will be used in the WireGuard server configuration.

For the examples below, the interface is assumed to be eth0.


Step 4: Generate the Server Key Pair

Create a secure directory for the WireGuard configuration:

sudo install -d -m 700 /etc/wireguard

Set a restrictive file-creation mask:

umask 077

Generate the server’s private key:

wg genkey | sudo tee /etc/wireguard/server_private.key > /dev/null

Generate the corresponding public key:

sudo cat /etc/wireguard/server_private.key \
  | wg pubkey \
  | sudo tee /etc/wireguard/server_public.key > /dev/null

Verify the file permissions:

sudo ls -l /etc/wireguard/server_*.key

The private key should not be readable by unprivileged users.

Display the server public key:

sudo cat /etc/wireguard/server_public.key

Save this public key. It will be required when configuring the client.

Do not disclose the contents of:

/etc/wireguard/server_private.key

WireGuard uses public-key authentication between peers. Each peer keeps its private key secret and shares only its public key.


Step 5: Enable IPv4 Forwarding

The server must forward packets between the WireGuard interface and the external network interface.

Create a dedicated sysctl configuration file:

sudo nano /etc/sysctl.d/99-wireguard.conf

Add:

net.ipv4.ip_forward = 1

Save the file and apply the setting:

sudo sysctl --system

Verify it:

sysctl net.ipv4.ip_forward

Expected output:

net.ipv4.ip_forward = 1

Using /etc/sysctl.d/99-wireguard.conf is preferable to modifying the main /etc/sysctl.conf file because it keeps the WireGuard-specific setting isolated and easier to manage.


Step 6: Create the WireGuard Server Configuration

Read the server private key:

sudo cat /etc/wireguard/server_private.key

Create the interface configuration:

sudo nano /etc/wireguard/wg0.conf

Add the following configuration:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY

PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

Replace:

SERVER_PRIVATE_KEY

with the actual contents of:

/etc/wireguard/server_private.key

Also replace eth0 with the server’s actual external interface.

For example:

PrivateKey = ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcd=

The private key must appear directly in the configuration. Do not enter a file path such as:

PrivateKey = /etc/wireguard/server_private.key

The PrivateKey field expects the key itself, not the name of the file containing it.

Configuration explanation

  • Address assigns an IP address to the WireGuard interface.
  • ListenPort specifies the UDP port on which the server listens.
  • PrivateKey identifies and authenticates the server.
  • PostUp runs firewall and NAT commands when the interface starts.
  • PostDown removes those rules when the interface stops.
  • %i is replaced automatically by the current WireGuard interface name.
  • MASQUERADE translates VPN client addresses to the server’s external address.

The original configuration used:

Address = 10.0.0.1

It should include a subnet prefix:

Address = 10.8.0.1/24

Without the prefix, interface addressing and route creation may not behave as intended.

Secure the configuration file:

sudo chmod 600 /etc/wireguard/wg0.conf

Check the permissions:

sudo ls -l /etc/wireguard/wg0.conf

Step 7: Configure the Server Firewall

UFW configuration

Check whether UFW is active:

sudo ufw status verbose

Allow WireGuard’s UDP port:

sudo ufw allow 51820/udp

If SSH access is not already permitted, allow it before enabling UFW:

sudo ufw allow OpenSSH

If the SSH service uses a custom port, allow that port instead. For example:

sudo ufw allow 2222/tcp

Allow forwarded traffic from the WireGuard interface to the server’s external interface:

sudo ufw route allow in on wg0 out on eth0

Allow return traffic in the opposite direction:

sudo ufw route allow in on eth0 out on wg0

Replace eth0 with the actual external interface.

Reload UFW:

sudo ufw reload

Check the resulting rules:

sudo ufw status numbered

Ubuntu documents UFW as its standard host firewall management tool.

Cloud firewall configuration

If the server runs on AWS, Azure, Google Cloud, Oracle Cloud, DigitalOcean, Vultr, Linode, or another VPS platform, also allow this inbound rule in the provider’s firewall:

Protocol: UDP
Port: 51820
Source: 0.0.0.0/0

For better security, restrict the source address when the client connects from a known static IP.

Opening the port with UFW does not automatically open it in an external cloud firewall.


Step 8: Validate the Server Configuration

Before starting WireGuard, test the configuration by bringing the interface up manually:

sudo wg-quick up wg0

Check the interface:

sudo wg show

Check its IP address:

ip address show wg0

Check the generated route:

ip route show

Expected interface information includes:

interface: wg0
  public key: SERVER_PUBLIC_KEY
  private key: (hidden)
  listening port: 51820

Take the interface down after testing:

sudo wg-quick down wg0

The wg-quick utility reads /etc/wireguard/wg0.conf, creates the interface, assigns its addresses and configures the required routes.


Step 9: Start and Enable WireGuard

Start the interface through systemd:

sudo systemctl start wg-quick@wg0

Enable it at boot:

sudo systemctl enable wg-quick@wg0

Alternatively, perform both operations with:

sudo systemctl enable --now wg-quick@wg0

Check the service status:

sudo systemctl status wg-quick@wg0 --no-pager

Check that the server is listening on UDP port 51820:

sudo ss -lunp | grep 51820

The original command:

sudo systemctl start wg-quick@wg0.servicesudo systemctl enable wg-quick@wg0.service

is invalid because it contains two commands without a newline or command separator.

The .service suffix is optional. The following two forms are equivalent:

sudo systemctl restart wg-quick@wg0
sudo systemctl restart wg-quick@wg0.service

Part 2: Configure the WireGuard Client

The following steps assume that the client also runs Ubuntu, Debian, or Linux Mint.

Step 10: Install WireGuard on the Client

On the client machine, update the package index:

sudo apt update

Install WireGuard:

sudo apt install wireguard -y

Verify the installation:

wg --version

Step 11: Generate the Client Key Pair

Create the configuration directory:

sudo install -d -m 700 /etc/wireguard

Set restrictive permissions:

umask 077

Generate the client private key:

wg genkey | sudo tee /etc/wireguard/client_private.key > /dev/null

Generate the client public key:

sudo cat /etc/wireguard/client_private.key \
  | wg pubkey \
  | sudo tee /etc/wireguard/client_public.key > /dev/null

Display the client public key:

sudo cat /etc/wireguard/client_public.key

Copy this value. It must be added to the server configuration.

Display the client private key:

sudo cat /etc/wireguard/client_private.key

The client private key will be added only to the client configuration. Never copy it to the server.


Step 12: Add the Client as a Peer on the Server

Return to the WireGuard server and edit:

sudo nano /etc/wireguard/wg0.conf

Append:

[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32

Replace CLIENT_PUBLIC_KEY with the public key generated on the client.

The completed server configuration should resemble:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY

PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32

On the server, AllowedIPs serves two important purposes:

  1. It associates 10.8.0.2 with this client’s public key.
  2. It tells WireGuard which peer should receive packets destined for 10.8.0.2.

Each client must have a unique VPN address. Do not assign 10.8.0.2 to multiple peers.

Restart WireGuard:

sudo systemctl restart wg-quick@wg0

Verify the peer:

sudo wg show

Step 13: Create the Client Configuration

On the client, obtain the server public key:

sudo cat /etc/wireguard/server_public.key

Run this command on the server, not the client, unless you have copied only the server public key to the client.

On the client, create:

sudo nano /etc/wireguard/wg0.conf

Add:

[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.8.0.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Replace:

  • CLIENT_PRIVATE_KEY with the client private key.
  • SERVER_PUBLIC_KEY with the server public key.
  • 203.0.113.10 with the server’s public IP address or hostname.

For example:

Endpoint = vpn.example.com:51820

Secure the configuration file:

sudo chmod 600 /etc/wireguard/wg0.conf

Client configuration explanation

Address

Address = 10.8.0.2/24

This assigns the client its VPN address.

The original example used:

Address = 10.0.0.2

The subnet prefix should be included.

DNS

DNS = 1.1.1.1

This configures a DNS resolver while the tunnel is active.

Other possible DNS resolvers include:

DNS = 8.8.8.8

or a private DNS server reachable through the VPN.

The DNS directive requires a compatible DNS management utility, commonly resolvconf or systemd-resolved. If wg-quick reports a resolvconf: command not found error, install it:

sudo apt install resolvconf -y

Alternatively, remove the DNS line and manage DNS separately.

Endpoint

Endpoint = 203.0.113.10:51820

This specifies the public IP address or DNS hostname of the VPN server and its UDP listening port.

AllowedIPs

AllowedIPs = 0.0.0.0/0

This creates a full-tunnel IPv4 configuration, causing all IPv4 traffic to use the VPN.

For split tunneling, route only selected networks. For example:

AllowedIPs = 10.8.0.0/24

This sends only traffic destined for the WireGuard VPN network through the tunnel.

To access both the VPN network and a private remote LAN, you could use:

AllowedIPs = 10.8.0.0/24, 192.168.50.0/24

WireGuard’s wg-quick utility can automatically create routes based on the networks listed in AllowedIPs, including special handling for default routes.

PersistentKeepalive

PersistentKeepalive = 25

This is useful when the client is behind NAT or a stateful firewall. It periodically sends an authenticated packet to keep the NAT mapping active.

It is normally configured on the client peer, not on a publicly reachable server.


Part 3: Start and Test the VPN Client

Step 14: Bring Up the Client Interface

Start WireGuard manually:

sudo wg-quick up wg0

Check the interface:

sudo wg show

Check its address:

ip address show wg0

You should see:

inet 10.8.0.2/24

Check the route configuration:

ip route show

Because this is a full-tunnel configuration, wg-quick may use policy routing rather than replacing the visible main default route directly.


Step 15: Verify Connectivity

Test the WireGuard server’s VPN address

From the client:

ping -c 4 10.8.0.1

A successful response confirms that packets can travel through the tunnel.

Check the WireGuard handshake

On either the server or client:

sudo wg show

Look for:

latest handshake
transfer

Example:

latest handshake: 15 seconds ago
transfer: 24.31 KiB received, 18.72 KiB sent

No latest handshake entry usually means that the server and client have not successfully authenticated and exchanged packets.

Verify the client’s public IP address

Before connecting, you can check the client’s normal public IP:

curl -4 https://ifconfig.me

After bringing up WireGuard, run the command again:

curl -4 https://ifconfig.me

The result should now match the public IP address of the WireGuard server.

Test DNS resolution

Run:

getent hosts example.com

You can also test HTTPS connectivity:

curl -I https://example.com

Step 16: Enable WireGuard at Boot on the Client

Enable and start the client interface:

sudo systemctl enable --now wg-quick@wg0

Check its status:

sudo systemctl status wg-quick@wg0 --no-pager

To disable automatic startup later:

sudo systemctl disable wg-quick@wg0

Managing the WireGuard Connection

Disconnect the client

sudo wg-quick down wg0

Reconnect the client

sudo wg-quick up wg0

Restart the systemd service

sudo systemctl restart wg-quick@wg0

Display the current configuration

sudo wg show

Display only WireGuard interfaces

ip link show type wireguard

Monitor handshakes and traffic continuously

watch -n 2 sudo wg show

Ubuntu’s WireGuard troubleshooting documentation also recommends monitoring wg output when diagnosing peer connectivity and handshake issues.


Adding More WireGuard Clients

Each additional client requires:

  • A unique private and public key pair.
  • A unique VPN IP address.
  • A separate [Peer] block on the server.

For example, a second client could use:

10.8.0.3

Add the following block to the server:

[Peer]
PublicKey = SECOND_CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.3/32

The second client configuration would contain:

[Interface]
PrivateKey = SECOND_CLIENT_PRIVATE_KEY
Address = 10.8.0.3/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Restart the server interface after editing the configuration:

sudo systemctl restart wg-quick@wg0

Never reuse a private key or VPN IP address across multiple clients.


Optional: Apply Peer Changes Without Interrupting Existing Connections

Restarting wg-quick@wg0 briefly recreates the interface. On a busy VPN server, you may apply peer changes without taking the interface down.

First verify that the configuration does not contain unsupported values:

sudo wg-quick strip wg0

Then synchronize the running WireGuard configuration:

sudo wg syncconf wg0 <(sudo wg-quick strip wg0)

This command requires a shell that supports process substitution, such as Bash.

Alternatively, add a peer directly:

sudo wg set wg0 peer CLIENT_PUBLIC_KEY allowed-ips 10.8.0.2/32

Changes made only with wg set are not automatically written to /etc/wireguard/wg0.conf, so they may be lost after a reboot unless the configuration file is also updated.


Troubleshooting WireGuard

Problem 1: No WireGuard handshake

Run on the server:

sudo wg show

Check whether UDP port 51820 is listening:

sudo ss -lunp | grep 51820

Check UFW:

sudo ufw status numbered

Confirm that the cloud provider’s firewall also allows UDP port 51820.

Check the server logs:

sudo journalctl -u wg-quick@wg0 --no-pager

Check recent logs continuously:

sudo journalctl -u wg-quick@wg0 -f

Common causes include:

  • An incorrect server endpoint.
  • TCP port 51820 opened instead of UDP.
  • A blocked cloud firewall rule.
  • Incorrect public or private keys.
  • The server service is not running.
  • WireGuard is listening on a different port.
  • The client is using an outdated server public key.

Problem 2: Handshake succeeds, but there is no internet access

Check IP forwarding on the server:

sysctl net.ipv4.ip_forward

It must return:

net.ipv4.ip_forward = 1

Check the NAT rule:

sudo iptables -t nat -L POSTROUTING -n -v

Check forwarding rules:

sudo iptables -L FORWARD -n -v

Confirm that the external interface in wg0.conf is correct:

ip route show default

For example, this rule will not work if the actual external interface is ens3 but the configuration uses eth0:

PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

Correct it and restart WireGuard:

sudo systemctl restart wg-quick@wg0

Problem 3: The VPN works by IP address but DNS fails

Test direct IP connectivity:

ping -c 4 1.1.1.1

Then test DNS:

getent hosts example.com

If the first command succeeds but the second fails, the problem is DNS-related.

Check whether the client configuration contains:

DNS = 1.1.1.1

If wg-quick reports that resolvconf is missing, install it:

sudo apt install resolvconf -y

Restart the interface:

sudo wg-quick down wg0
sudo wg-quick up wg0

Problem 4: wg-quick up wg0 reports that the interface already exists

Example error:

wg-quick: `wg0' already exists

Check the interface:

ip link show wg0

Bring it down:

sudo wg-quick down wg0

If that fails, remove the interface manually:

sudo ip link delete wg0

Then start it again:

sudo wg-quick up wg0

Problem 5: The service fails after editing wg0.conf

Check the service status:

sudo systemctl status wg-quick@wg0 --no-pager

View the detailed logs:

sudo journalctl -xeu wg-quick@wg0

Common configuration errors include:

  • Missing private keys.
  • Keys containing extra spaces or line breaks.
  • Invalid IP addresses.
  • Missing CIDR prefixes.
  • Duplicate VPN addresses.
  • Incorrect PostUp or PostDown commands.
  • A DNS directive without a compatible DNS helper.
  • A public key mistakenly placed in the PrivateKey field.

Problem 6: SSH disconnects when the VPN client starts

A full-tunnel configuration uses:

AllowedIPs = 0.0.0.0/0

This changes how the client routes internet traffic. If you are configuring WireGuard on a remote machine through SSH, the SSH reply traffic may be redirected through the tunnel.

To test safely, first use split tunneling:

AllowedIPs = 10.8.0.0/24

After confirming that the tunnel works, carefully change it to:

AllowedIPs = 0.0.0.0/0

Always keep an alternative console or recovery method available when changing routes on a remote system.


Security Recommendations

Protect private keys

WireGuard configuration files should be readable only by root:

sudo chmod 600 /etc/wireguard/*.conf
sudo chmod 600 /etc/wireguard/*.key

Check them:

sudo find /etc/wireguard -maxdepth 1 -type f -ls

Never send private keys through email, chat, tickets, logs, or public repositories.

Restrict the VPN port when possible

If clients connect from fixed public IP addresses, restrict UDP port 51820:

sudo ufw delete allow 51820/udp
sudo ufw allow from CLIENT_PUBLIC_IP to any port 51820 proto udp

Do not use this restriction for clients whose public IP addresses change frequently.

Use a separate key for every device

Do not copy one client configuration to several devices. Unique keys make it possible to revoke one device without affecting other users.

Remove unused peers

Delete inactive [Peer] blocks from the server configuration and restart or synchronize the interface.

Keep the operating system updated

Install security updates regularly:

sudo apt update
sudo apt upgrade -y

Do not expose the private key through command history

Avoid placing private keys directly in shell commands. Store them in protected configuration files and verify that those files have restrictive permissions.


Complete Server Configuration Example

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY

PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32

Complete Client Configuration Example

[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.8.0.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Final Verification Checklist

On the server, verify:

sysctl net.ipv4.ip_forward
sudo systemctl status wg-quick@wg0 --no-pager
sudo wg show
sudo ss -lunp | grep 51820
sudo iptables -t nat -L POSTROUTING -n -v
sudo ufw status verbose

On the client, verify:

sudo systemctl status wg-quick@wg0 --no-pager
sudo wg show
ip address show wg0
ping -c 4 10.8.0.1
curl -4 https://ifconfig.me
getent hosts example.com

A working installation should show:

  • The wg0 interface is active on both systems.
  • The client and server have a recent WireGuard handshake.
  • Transfer counters increase when traffic is generated.
  • The client can reach 10.8.0.1.
  • DNS resolution works.
  • The client’s public IPv4 address matches the VPN server when full tunneling is enabled.

Conclusion

You have now installed and configured a WireGuard VPN server and Linux client on Ubuntu, Debian, or Linux Mint.

The server listens for encrypted WireGuard traffic on UDP port 51820, authenticates the client using public keys, forwards client packets and performs NAT through its external network interface. The client uses AllowedIPs = 0.0.0.0/0 to route all IPv4 traffic through the VPN.

For split tunneling, replace the client’s default-route entry with only the private networks that should be reachable through WireGuard.