Overview
This step-by-step guide shows you how to install and secure a WireGuard VPN on Ubuntu 24.04 LTS. You will enable IP forwarding, add NAT, configure DNS, generate mobile-friendly QR codes, and set up a client kill switch on Linux. WireGuard is fast, modern, and simple—perfect for remote access, privacy, and self-hosted lab networks.
What you need
You need an Ubuntu 24.04 server with sudo, a public IP (or port-forwarded UDP 51820), and a basic understanding of the terminal. Replace interface names like eth0 with your actual WAN NIC (for example, ens3 or enp1s0), and replace placeholders like SERVER_PUBLIC_IP with your values.
1) Install WireGuard and tools
Update the package index, then install WireGuard, UFW (if you use it), and a small utility to generate QR codes for mobile clients.
sudo apt update
sudo apt install -y wireguard qrencode ufw
2) Generate server keys
Create a private and public key for the server. The private key must stay secret and readable only by root.
sudo -i
umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
cat /etc/wireguard/server.key
cat /etc/wireguard/server.pub
Copy the printed keys to use in the configuration files below. Do not share the private key with anyone.
3) Enable IP forwarding
Turn on IPv4 and IPv6 forwarding so the server can route traffic from clients to the Internet.
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
echo 'net.ipv6.conf.all.forwarding=1' | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
4) Create the WireGuard server config
Create /etc/wireguard/wg0.conf. Replace SERVER_PRIVATE_KEY with the contents of /etc/wireguard/server.key, and replace eth0 with your external interface.
sudo nano /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
SaveConfig = true
MTU = 1420
# NAT and forwarding rules (iptables-nft works on Ubuntu 24.04)
# Replace eth0 with your WAN NIC (e.g., ens3)
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; \
iptables -A FORWARD -i %i -j ACCEPT; \
iptables -A FORWARD -o %i -m state --state RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; \
iptables -D FORWARD -i %i -j ACCEPT; \
iptables -D FORWARD -o %i -m state --state RELATED,ESTABLISHED -j ACCEPT
Allow UDP 51820 on the firewall, then start and enable the service.
sudo ufw allow 51820/udp
sudo systemctl enable --now wg-quick@wg0
sudo wg
5) Add your first peer (client)
Generate a client keypair on the server (safe for a single user or a small team). Each client must have a unique address.
cd /root
umask 077
wg genkey | tee client1.key | wg pubkey > client1.pub
CLIENT_PRIV=$(cat client1.key)
CLIENT_PUB=$(cat client1.pub)
SERVER_PUB=$(cat /etc/wireguard/server.pub)
Append a [Peer] block for the client to /etc/wireguard/wg0.conf. This line assigns 10.8.0.2/32 to the client and restricts it tightly.
sudo bash -c 'cat >> /etc/wireguard/wg0.conf' << 'EOF'
[Peer]
# client1
PublicKey = REPLACE_WITH_CLIENT1_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32
EOF
Apply the change without downtime:
sudo wg syncconf wg0 <(wg-quick strip wg0)
6) Build the client config (with DNS)
Create a client configuration file that routes all traffic through the VPN and uses secure DNS. Replace SERVER_PUBLIC_IP or domain and keep port 51820/udp open. Linux, macOS, Windows, iOS, and Android all accept this format.
cat > client1.conf << EOF
[Interface]
Address = 10.8.0.2/32
PrivateKey = CLIENT1_PRIVATE_KEY
DNS = 1.1.1.1, 9.9.9.9
MTU = 1420
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = SERVER_PUBLIC_IP:51820
PersistentKeepalive = 25
EOF
7) Show a QR code for mobile apps
Install the WireGuard app on iOS or Android, then scan this QR code displayed in your terminal to import the profile instantly.
qrencode -t ansiutf8 < client1.conf
If your terminal does not render Unicode well, save a PNG with qrencode -o client1.png < client1.conf and open it locally.
8) Optional: Linux client kill switch
A kill switch blocks all traffic outside the VPN. The rules below let only the WireGuard handshake to the server bypass the tunnel. Add these to the client’s client1.conf, replacing SERVER_PUBLIC_IP if you use an IP and not a domain.
# Allow the handshake, then drop everything not via wg
PreUp = iptables -I OUTPUT -d SERVER_PUBLIC_IP -p udp --dport 51820 -j ACCEPT
PreUp = iptables -I OUTPUT ! -o %i -m addrtype ! --dst-type LOCAL -j DROP
PreUp = ip6tables -I OUTPUT ! -o %i -m addrtype ! --dst-type LOCAL -j DROP
PostDown = iptables -D OUTPUT ! -o %i -m addrtype ! --dst-type LOCAL -j DROP
PostDown = ip6tables -D OUTPUT ! -o %i -m addrtype ! --dst-type LOCAL -j DROP
PostDown = iptables -D OUTPUT -d SERVER_PUBLIC_IP -p udp --dport 51820 -j ACCEPT
On Android, you can also enable “Block connections without VPN” in system settings after importing the profile.
9) Test the tunnel
Connect your client and verify that your public IP changes to the server. You should also be able to reach private services across the tunnel.
# On the client
wg
curl -s https://ifconfig.me
dig +short txt ch whoami.cloudflare @1.1.1.1
Troubleshooting tips
If the client does not connect, verify that UDP 51820 is open and forwarded, confirm your external interface name in PostUp/PostDown, and ensure AllowedIPs on the server exactly match each client’s single /32 address. Lower MTU (e.g., 1280) if you observe stalls, especially behind cellular or PPPoE links. If your provider uses CGNAT and you cannot forward UDP, host the server on a VPS with a public IP.
Security and maintenance
Rotate keys periodically, remove unused peers, and keep Ubuntu updated. Use strong DNS resolvers or your own recursive resolver for privacy. For teams, restrict each peer with precise AllowedIPs and consider splitting traffic by adding only the subnets you need. Monitor sudo wg output to audit connected peers and data transferred.
You now have a modern, efficient WireGuard VPN on Ubuntu 24.04 with DNS, mobile QR onboarding, and an optional kill switch. This setup is fast enough for home labs and secure enough for remote teams.
Comments
Post a Comment