Tools: Set Up WireGuard VPN on Ubuntu 24.04: Secure Private Access - Full Analysis

Tools: Set Up WireGuard VPN on Ubuntu 24.04: Secure Private Access - Full Analysis

Prerequisites

Step 1 — Update the System and Install WireGuard

Step 2 — Generate Server and Client Key Pairs

Step 3 — Configure the WireGuard Server Interface

Step 4 — Enable IP Forwarding

Step 5 — Open the Firewall for WireGuard

Step 6 — Start WireGuard and Enable Autostart

Step 7 — Configure the Client

Full Tunnel vs Split Tunnel

Step 8 — Verify the Tunnel

Useful WireGuard Commands

Common Troubleshooting Checks

1. Is WireGuard listening?

2. Is the firewall open?

3. Is IP forwarding enabled?

4. Are the keys correct?

5. Is the endpoint correct?

6. Are the AllowedIPs correct?

Conclusion WireGuard is a modern VPN protocol for creating encrypted tunnels between servers, laptops, and other devices. It is fast, lightweight, and built directly into the Linux kernel, making it one of the simplest ways to secure access to a VPS or cloud server. In this tutorial, you will install WireGuard on Ubuntu 24.04, generate server and client key pairs, configure the wg0 interface, enable IP forwarding, open the firewall, configure a client, and verify that the encrypted tunnel is working. This setup uses a common “road warrior” configuration: one server and one or more remote clients. Each client receives a private VPN IP address in the 10.0.0.0/24 range and connects to the server through an encrypted WireGuard tunnel. Start by updating your package index and installing WireGuard. On Ubuntu 24.04, WireGuard is available from the standard apt repository, so no external PPA is required. WireGuard installs the wg command-line tool and the wg-quick helper, which manages the interface lifecycle. Verify the installation: The version number may differ slightly, but any v1.0.x build is fine. If the command is not found, load the WireGuard kernel module and retry: Ubuntu 24.04 ships with a modern Linux kernel that includes WireGuard support natively, so you normally do not need to install any separate kernel modules. WireGuard uses asymmetric Curve25519 key pairs. Each peer needs its own private key and public key. The private key must remain secret. The public key is shared with the other peer. On the server, generate the server key pair: Lock down the private key file so only root can read it: Now generate the client key pair. You can generate this directly on the client machine, which is safer, or generate it on the server and transfer it securely. For simplicity, this example generates both on the server: Display the keys so you can use them in the configuration files: Keep these values available for the next steps. Never share or commit private keys. If a private key is exposed, regenerate the key pair and update all peer configurations immediately. Create the server configuration file at: First, identify your server’s public-facing network interface. The interface name appears after dev. In this example, it is eth0. You will use this interface name in the PostUp and PostDown rules. Open the WireGuard server configuration file: Paste the following configuration and replace the placeholders with your actual keys: If your public-facing interface is not eth0, replace eth0 in the PostUp and PostDown lines with the correct interface name. Save and exit the file. If you plan to support multiple clients, add a separate [Peer] block for each client. Each client should receive a unique VPN IP address, such as: WireGuard needs the Linux kernel to forward packets between interfaces. Open the system configuration file: Uncomment it by removing the #: Apply the change without rebooting: Without IP forwarding, the client may connect to the VPN but fail to reach networks beyond the server itself. WireGuard uses UDP port 51820 by default. If you are using UFW, allow WireGuard traffic: Expected output should include: For production environments, restrict the allowed source IPs for port 51820 to known client IP ranges when possible. Replace 203.0.113.25 with your trusted client IP address. Bring the WireGuard interface up: Enable WireGuard to start automatically on boot: Verify that the interface is running: At this stage, no peer handshake will appear yet. That happens after the client connects. On your client machine, create a WireGuard configuration. For Linux clients, create: For macOS, Windows, iOS, or Android, you can paste this configuration into the WireGuard app. Use the following client configuration: Replace <your-server-public-ip> with your server’s public IPv4 address. On a Linux client, bring the tunnel up: On macOS or Windows, import the configuration into the WireGuard app and click Activate. This configuration uses a full tunnel: That routes all client traffic through the VPN. If you only want to route traffic destined for the VPN subnet, use a split tunnel instead: A split tunnel keeps normal internet traffic on the client’s local connection while routing only VPN traffic through WireGuard. Back on the server, check for a client handshake: After the client connects, you should see output similar to this: The latest handshake line confirms that the encrypted session is active. From the client, ping the server’s VPN IP: If the ping succeeds, your WireGuard tunnel is working. You can now connect to the server over its private VPN IP instead of relying on public access: After confirming VPN access works, consider restricting SSH to the VPN interface only. For example, you can configure SSH to listen on the WireGuard IP by adding this to /etc/ssh/sshd_config: Be careful when changing SSH settings. Keep your existing session open until you confirm that a new VPN-based SSH connection works. Show the current WireGuard status: Bring the tunnel down: Check whether WireGuard starts on boot: View WireGuard service logs: If the client does not connect, check the following. You should see interface: wg0 and listening port: 51820. Make sure UDP port 51820 is allowed. Private keys should never be shared between peers. In the client config, confirm that the endpoint is your server’s public IP and WireGuard port: On the server, the client peer should usually use: On the client, full tunnel mode uses: Split tunnel mode uses: You have installed WireGuard on Ubuntu 24.04, generated server and client key pairs, configured the wg0 interface, enabled IP forwarding, opened UDP port 51820, configured a client, and verified the encrypted tunnel. This setup gives you private access to your server through the 10.0.0.0/24 VPN subnet. Instead of exposing SSH, internal dashboards, databases, or admin services directly to the public internet, you can route administrative access through WireGuard. Natural next steps include: WireGuard is now ready to provide secure private access to your Ubuntu 24.04 server. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install wireguard -y -weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install wireguard -y -weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install wireguard -y wg --version wg --version wg --version wireguard-tools v1.0.20210914 - https://-weight: 500;">git.zx2c4.com/wireguard-tools/ wireguard-tools v1.0.20210914 - https://-weight: 500;">git.zx2c4.com/wireguard-tools/ wireguard-tools v1.0.20210914 - https://-weight: 500;">git.zx2c4.com/wireguard-tools/ -weight: 600;">sudo modprobe wireguard wg --version -weight: 600;">sudo modprobe wireguard wg --version -weight: 600;">sudo modprobe wireguard wg --version wg genkey | -weight: 600;">sudo tee /etc/wireguard/server_private.key | wg pubkey | -weight: 600;">sudo tee /etc/wireguard/server_public.key wg genkey | -weight: 600;">sudo tee /etc/wireguard/server_private.key | wg pubkey | -weight: 600;">sudo tee /etc/wireguard/server_public.key wg genkey | -weight: 600;">sudo tee /etc/wireguard/server_private.key | wg pubkey | -weight: 600;">sudo tee /etc/wireguard/server_public.key -weight: 600;">sudo chmod 600 /etc/wireguard/server_private.key -weight: 600;">sudo chmod 600 /etc/wireguard/server_private.key -weight: 600;">sudo chmod 600 /etc/wireguard/server_private.key wg genkey | -weight: 600;">sudo tee /etc/wireguard/client_private.key | wg pubkey | -weight: 600;">sudo tee /etc/wireguard/client_public.key -weight: 600;">sudo chmod 600 /etc/wireguard/client_private.key wg genkey | -weight: 600;">sudo tee /etc/wireguard/client_private.key | wg pubkey | -weight: 600;">sudo tee /etc/wireguard/client_public.key -weight: 600;">sudo chmod 600 /etc/wireguard/client_private.key wg genkey | -weight: 600;">sudo tee /etc/wireguard/client_private.key | wg pubkey | -weight: 600;">sudo tee /etc/wireguard/client_public.key -weight: 600;">sudo chmod 600 /etc/wireguard/client_private.key -weight: 600;">sudo cat /etc/wireguard/server_private.key -weight: 600;">sudo cat /etc/wireguard/server_public.key -weight: 600;">sudo cat /etc/wireguard/client_private.key -weight: 600;">sudo cat /etc/wireguard/client_public.key -weight: 600;">sudo cat /etc/wireguard/server_private.key -weight: 600;">sudo cat /etc/wireguard/server_public.key -weight: 600;">sudo cat /etc/wireguard/client_private.key -weight: 600;">sudo cat /etc/wireguard/client_public.key -weight: 600;">sudo cat /etc/wireguard/server_private.key -weight: 600;">sudo cat /etc/wireguard/server_public.key -weight: 600;">sudo cat /etc/wireguard/client_private.key -weight: 600;">sudo cat /etc/wireguard/client_public.key /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf ip route | grep default ip route | grep default ip route | grep default default via 203.0.113.1 dev eth0 proto dhcp default via 203.0.113.1 dev eth0 proto dhcp default via 203.0.113.1 dev eth0 proto dhcp -weight: 600;">sudo nano /etc/wireguard/wg0.conf -weight: 600;">sudo nano /etc/wireguard/wg0.conf -weight: 600;">sudo nano /etc/wireguard/wg0.conf [Interface] # Server private key PrivateKey = <paste-server-private-key-here> # VPN IP address assigned to the server Address = 10.0.0.1/24 # WireGuard listens on this UDP port ListenPort = 51820 # Enable IP masquerading so VPN clients can reach the internet PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] # Client public key PublicKey = <paste-client-public-key-here> # IP address assigned to this client inside the VPN AllowedIPs = 10.0.0.2/32 [Interface] # Server private key PrivateKey = <paste-server-private-key-here> # VPN IP address assigned to the server Address = 10.0.0.1/24 # WireGuard listens on this UDP port ListenPort = 51820 # Enable IP masquerading so VPN clients can reach the internet PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] # Client public key PublicKey = <paste-client-public-key-here> # IP address assigned to this client inside the VPN AllowedIPs = 10.0.0.2/32 [Interface] # Server private key PrivateKey = <paste-server-private-key-here> # VPN IP address assigned to the server Address = 10.0.0.1/24 # WireGuard listens on this UDP port ListenPort = 51820 # Enable IP masquerading so VPN clients can reach the internet PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] # Client public key PublicKey = <paste-client-public-key-here> # IP address assigned to this client inside the VPN AllowedIPs = 10.0.0.2/32 10.0.0.3/32 10.0.0.4/32 10.0.0.5/32 10.0.0.3/32 10.0.0.4/32 10.0.0.5/32 10.0.0.3/32 10.0.0.4/32 10.0.0.5/32 -weight: 600;">sudo nano /etc/sysctl.conf -weight: 600;">sudo nano /etc/sysctl.conf -weight: 600;">sudo nano /etc/sysctl.conf #net.ipv4.ip_forward=1 #net.ipv4.ip_forward=1 #net.ipv4.ip_forward=1 net.ipv4.ip_forward=1 net.ipv4.ip_forward=1 net.ipv4.ip_forward=1 -weight: 600;">sudo sysctl -p -weight: 600;">sudo sysctl -p -weight: 600;">sudo sysctl -p net.ipv4.ip_forward = 1 net.ipv4.ip_forward = 1 net.ipv4.ip_forward = 1 -weight: 600;">sudo ufw allow 51820/udp -weight: 600;">sudo ufw -weight: 500;">status -weight: 600;">sudo ufw allow 51820/udp -weight: 600;">sudo ufw -weight: 500;">status -weight: 600;">sudo ufw allow 51820/udp -weight: 600;">sudo ufw -weight: 500;">status 51820/udp ALLOW Anywhere 51820/udp (v6) ALLOW Anywhere (v6) 51820/udp ALLOW Anywhere 51820/udp (v6) ALLOW Anywhere (v6) 51820/udp ALLOW Anywhere 51820/udp (v6) ALLOW Anywhere (v6) -weight: 600;">sudo ufw allow from 203.0.113.25 to any port 51820 proto udp -weight: 600;">sudo ufw allow from 203.0.113.25 to any port 51820 proto udp -weight: 600;">sudo ufw allow from 203.0.113.25 to any port 51820 proto udp -weight: 600;">sudo wg-quick up wg0 -weight: 600;">sudo wg-quick up wg0 -weight: 600;">sudo wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip -4 address add 10.0.0.1/24 dev wg0 [#] ip link set mtu 1420 up dev wg0 [#] iptables -A FORWARD -i wg0 -j ACCEPT; ... [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip -4 address add 10.0.0.1/24 dev wg0 [#] ip link set mtu 1420 up dev wg0 [#] iptables -A FORWARD -i wg0 -j ACCEPT; ... [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip -4 address add 10.0.0.1/24 dev wg0 [#] ip link set mtu 1420 up dev wg0 [#] iptables -A FORWARD -i wg0 -j ACCEPT; ... -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable wg-quick@wg0 -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable wg-quick@wg0 -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable wg-quick@wg0 -weight: 600;">sudo wg show -weight: 600;">sudo wg show -weight: 600;">sudo wg show interface: wg0 public key: <your-server-public-key> private key: (hidden) listening port: 51820 interface: wg0 public key: <your-server-public-key> private key: (hidden) listening port: 51820 interface: wg0 public key: <your-server-public-key> private key: (hidden) listening port: 51820 /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf [Interface] # Client private key PrivateKey = <paste-client-private-key-here> # IP address assigned to this client inside the VPN Address = 10.0.0.2/24 # Optional DNS resolver DNS = 1.1.1.1 [Peer] # Server public key PublicKey = <paste-server-public-key-here> # Server public IP address and WireGuard port Endpoint = <your-server-public-ip>:51820 # Route all traffic through the VPN AllowedIPs = 0.0.0.0/0 # Keep the tunnel alive through NAT PersistentKeepalive = 25 [Interface] # Client private key PrivateKey = <paste-client-private-key-here> # IP address assigned to this client inside the VPN Address = 10.0.0.2/24 # Optional DNS resolver DNS = 1.1.1.1 [Peer] # Server public key PublicKey = <paste-server-public-key-here> # Server public IP address and WireGuard port Endpoint = <your-server-public-ip>:51820 # Route all traffic through the VPN AllowedIPs = 0.0.0.0/0 # Keep the tunnel alive through NAT PersistentKeepalive = 25 [Interface] # Client private key PrivateKey = <paste-client-private-key-here> # IP address assigned to this client inside the VPN Address = 10.0.0.2/24 # Optional DNS resolver DNS = 1.1.1.1 [Peer] # Server public key PublicKey = <paste-server-public-key-here> # Server public IP address and WireGuard port Endpoint = <your-server-public-ip>:51820 # Route all traffic through the VPN AllowedIPs = 0.0.0.0/0 # Keep the tunnel alive through NAT PersistentKeepalive = 25 -weight: 600;">sudo wg-quick up wg0 -weight: 600;">sudo wg-quick up wg0 -weight: 600;">sudo wg-quick up wg0 AllowedIPs = 0.0.0.0/0 AllowedIPs = 0.0.0.0/0 AllowedIPs = 0.0.0.0/0 AllowedIPs = 10.0.0.0/24 AllowedIPs = 10.0.0.0/24 AllowedIPs = 10.0.0.0/24 -weight: 600;">sudo wg show -weight: 600;">sudo wg show -weight: 600;">sudo wg show interface: wg0 public key: <server-public-key> private key: (hidden) listening port: 51820 peer: <client-public-key> endpoint: <client-ip>:<ephemeral-port> allowed ips: 10.0.0.2/32 latest handshake: X seconds ago transfer: 1.23 KiB received, 4.56 KiB sent interface: wg0 public key: <server-public-key> private key: (hidden) listening port: 51820 peer: <client-public-key> endpoint: <client-ip>:<ephemeral-port> allowed ips: 10.0.0.2/32 latest handshake: X seconds ago transfer: 1.23 KiB received, 4.56 KiB sent interface: wg0 public key: <server-public-key> private key: (hidden) listening port: 51820 peer: <client-public-key> endpoint: <client-ip>:<ephemeral-port> allowed ips: 10.0.0.2/32 latest handshake: X seconds ago transfer: 1.23 KiB received, 4.56 KiB sent ping 10.0.0.1 ping 10.0.0.1 ping 10.0.0.1 PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=12.4 ms 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=11.9 ms PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=12.4 ms 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=11.9 ms PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=12.4 ms 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=11.9 ms ssh [email protected] ssh [email protected] ssh [email protected] ListenAddress 10.0.0.1 ListenAddress 10.0.0.1 ListenAddress 10.0.0.1 -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart ssh -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart ssh -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart ssh -weight: 600;">sudo wg show -weight: 600;">sudo wg show -weight: 600;">sudo wg show -weight: 600;">sudo wg-quick up wg0 -weight: 600;">sudo wg-quick up wg0 -weight: 600;">sudo wg-quick up wg0 -weight: 600;">sudo wg-quick down wg0 -weight: 600;">sudo wg-quick down wg0 -weight: 600;">sudo wg-quick down wg0 -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart wg-quick@wg0 -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart wg-quick@wg0 -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart wg-quick@wg0 -weight: 600;">sudo -weight: 500;">systemctl is-enabled wg-quick@wg0 -weight: 600;">sudo -weight: 500;">systemctl is-enabled wg-quick@wg0 -weight: 600;">sudo -weight: 500;">systemctl is-enabled wg-quick@wg0 -weight: 600;">sudo journalctl -u wg-quick@wg0 -weight: 600;">sudo journalctl -u wg-quick@wg0 -weight: 600;">sudo journalctl -u wg-quick@wg0 -weight: 600;">sudo journalctl -u wg-quick@wg0 -e -weight: 600;">sudo journalctl -u wg-quick@wg0 -e -weight: 600;">sudo journalctl -u wg-quick@wg0 -e -weight: 600;">sudo wg show -weight: 600;">sudo wg show -weight: 600;">sudo wg show -weight: 600;">sudo ufw -weight: 500;">status -weight: 600;">sudo ufw -weight: 500;">status -weight: 600;">sudo ufw -weight: 500;">status sysctl net.ipv4.ip_forward sysctl net.ipv4.ip_forward sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1 net.ipv4.ip_forward = 1 net.ipv4.ip_forward = 1 Endpoint = <your-server-public-ip>:51820 Endpoint = <your-server-public-ip>:51820 Endpoint = <your-server-public-ip>:51820 AllowedIPs = 10.0.0.2/32 AllowedIPs = 10.0.0.2/32 AllowedIPs = 10.0.0.2/32 AllowedIPs = 0.0.0.0/0 AllowedIPs = 0.0.0.0/0 AllowedIPs = 0.0.0.0/0 AllowedIPs = 10.0.0.0/24 AllowedIPs = 10.0.0.0/24 AllowedIPs = 10.0.0.0/24 - An Ubuntu 24.04 VPS or cloud server - SSH access to the server - A non-root user with -weight: 600;">sudo privileges - A second device, such as a laptop or another server, to act as the VPN client - UFW or another firewall configured on the server - The server config uses the server private key - The server peer block uses the client public key - The client config uses the client private key - The client peer block uses the server public key - Adding more clients with separate [Peer] blocks - Assigning each client a unique VPN IP address - Restricting SSH access to the WireGuard interface - Using split tunnel mode for admin-only access - Monitoring the tunnel with -weight: 600;">sudo wg show - Backing up /etc/wireguard/wg0.conf securely