Applications Azure

WireGuard on Ubuntu 24.04 on Azure User Guide

| Product: WireGuard VPN on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of WireGuard on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. WireGuard is a modern, fast, audited VPN protocol that lives in the Linux kernel from 5.6 onwards. It uses Curve25519 for key exchange, ChaCha20Poly1305 for data encryption, and presents a single UDP listener with a stateless config file — no PKI, no IKE/IPsec daemon, no certificates.

The image installs WireGuard from the official Ubuntu noble main repository (apt install wireguard wireguard-tools). The kernel module is in-tree on Ubuntu 24.04, so there is no DKMS or third-party PPA. At first boot, wireguard-firstboot.service generates a fresh per-VM Curve25519 server keypair, writes /etc/wireguard/wg0.conf (mode 0600, root only), starts wg-quick@wg0, and writes the server public key plus connection info to /stage/scripts/wireguard-credentials.log. The private key is never written outside wg0.conf.

The default deployment is server-only — no peer blocks are pre-configured. Customers add a [Peer] block per client device and reload. The default overlay is 10.50.0.0/24 with the server at 10.50.0.1. The listener binds UDP 51820. IP forwarding is enabled so peers can reach each other; NAT/MASQUERADE for full-tunnel internet egress is documented but commented out so you opt in deliberately.

What is included:

  • WireGuard kernel module + wg and wg-quick userspace tools from the Ubuntu noble main repo

  • wireguard-firstboot.service systemd oneshot that generates a per-VM Curve25519 server keypair on the first customer boot, writes /etc/wireguard/wg0.conf, and starts wg-quick@wg0

  • Default config at /etc/wireguard/wg0.conf with the server interface on 10.50.0.1/24, listening on UDP 51820, and an example [Peer] block commented in

  • wg-quick@wg0.service systemd unit auto-starting on boot

  • IP forwarding (net.ipv4.ip_forward=1, net.ipv6.conf.all.forwarding=1) enabled at install time via /etc/sysctl.d/99-wireguard.conf

  • Server public key + endpoint info file at /stage/scripts/wireguard-credentials.log (mode 0600, root only — share the public key with peer clients; never copy the private key)

  • Ubuntu 24.04 LTS base with latest security patches applied at build time

  • Azure Linux Agent for seamless cloud integration and SSH key injection

  • 24/7 cloudimg support with guaranteed 24 hour response SLA

Prerequisites

  • An active Azure subscription

  • A subscription to the WireGuard on Ubuntu 24.04 listing on Azure Marketplace

  • An SSH public key for VM authentication

  • A virtual network and subnet in the target region

  • Each peer client device must already have WireGuard installed (Linux kernel module, the official WireGuard app on macOS/iOS/Windows/Android, or the wireguard-tools package on a peer Linux box)

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM). WireGuard is extremely lightweight; the B2s burst credits are more than enough for typical small-team or branch-office overlays. Heavy site-to-site or hub-and-spoke deployments with >50 active peers should move to Standard_D2s_v3 or larger to ensure consistent CPU for crypto and to allow accelerated networking on the NIC.

Step 1: Deploy from the Azure Portal

Navigate to Marketplace in the Azure Portal, search for WireGuard, select the cloudimg publisher entry, and click Create.

On the Networking tab attach a network security group that allows inbound TCP 22 from your management IP range and inbound UDP 51820 from your peer client source IPs (or 0.0.0.0/0 if peers connect from the open internet — the WireGuard handshake itself is the access control). Do not open UDP 51820 to the world unless you accept that any internet host may probe the listener — WireGuard does not respond to handshakes from unknown peers, so this is safe in practice, but you may prefer to scope to known client networks.

Click Review + create, wait for validation, then Create. Deployment takes around two minutes.

Step 2: Deploy from the Azure CLI

RG="wireguard-prod"
LOCATION="eastus"
VM_NAME="wireguard-01"
ADMIN_USER="azureuser"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/wireguard-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"

az group create --name "$RG" --location "$LOCATION"

az network vnet create \
  --resource-group "$RG" \
  --name wg-vnet --address-prefix 10.96.0.0/16 \
  --subnet-name wg-subnet --subnet-prefix 10.96.1.0/24

az network nsg create --resource-group "$RG" --name wg-nsg

az network nsg rule create \
  --resource-group "$RG" --nsg-name wg-nsg \
  --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" \
  --destination-port-ranges 22 --access Allow --protocol Tcp

az network nsg rule create \
  --resource-group "$RG" --nsg-name wg-nsg \
  --name allow-wireguard --priority 110 \
  --source-address-prefixes "<your-client-cidr-or-internet>" \
  --destination-port-ranges 51820 --access Allow --protocol Udp

az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username "$ADMIN_USER" --ssh-key-values "$SSH_KEY" \
  --vnet-name wg-vnet --subnet wg-subnet --nsg wg-nsg \
  --public-ip-sku Standard

Step 3: Connect via SSH

ssh azureuser@<vm-ip>

wg-quick@wg0.service will already be running and wireguard-firstboot.service will already have generated the per-VM server keypair and written the public key to the credentials file.

Step 4: Verify the WireGuard Service

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

Expected: active (exited)wg-quick is a oneshot that brings the interface up and exits. Confirm the firstboot sentinel:

sudo test -f /var/lib/cloudimg/wireguard-firstboot.done && echo FIRSTBOOT_DONE

Confirm the interface and listener:

ip -brief addr show wg0
sudo wg show

wg-quick@wg0 active and wg show reporting the listening port + per-VM server public key

The wg show output reports the server public key (which you share with peers), the listening port (51820), and any peers (none on a fresh deployment).

Step 5: Read the Server Public Key

The server public key is written to a 0600 root-only file at first boot. Read it with:

sudo cat /stage/scripts/wireguard-credentials.log

Or grab just the key:

sudo grep '^SERVER_PUBLIC_KEY=' /stage/scripts/wireguard-credentials.log | cut -d= -f2-

/stage/scripts/wireguard-credentials.log showing per-VM server public key + connection parameters

You will share this public key (along with the VM's public Azure IP and UDP port 51820) with each peer device. The matching private key lives only in /etc/wireguard/wg0.conf and never leaves the VM.

Step 6: Generate a Peer Keypair on the Client

On each client device that should connect, generate a fresh keypair locally. Generate keys on the client, not on the server — the client's private key must never leave the client device.

On a Linux client:

wg genkey | tee client-priv.key | wg pubkey > client-pub.key
chmod 600 client-priv.key
cat client-pub.key

On macOS or Windows, install the official WireGuard app from https://www.wireguard.com/install/, click Add Tunnel → Add Empty Tunnel, and the app generates the keypair for you. Copy the displayed Public key.

Send the client public key (and only the public key) to the server administrator.

Step 7: Add a Peer Block on the Server

Edit /etc/wireguard/wg0.conf on the WireGuard VM and append a [Peer] block per client. Pick a unique address for each peer in the 10.50.0.0/24 overlay (skip 10.50.0.1, that is the server):

sudo tee -a /etc/wireguard/wg0.conf > /dev/null << 'PEER'

[Peer]
# Alice's laptop
PublicKey = <paste-client-public-key-here>
AllowedIPs = 10.50.0.2/32
PEER

Reload the interface to pick up the new peer without dropping existing tunnels:

sudo wg-quick strip wg0 | sudo wg syncconf wg0 /dev/stdin

Or restart it (briefly drops other peers):

sudo systemctl restart wg-quick@wg0

Confirm the new peer appears:

sudo wg show

sudo wg show after adding a peer block — peer public key, allowed IPs, latest handshake

Step 8: Configure the Client

Build the client config using the server's public key, the VM's public Azure IP, and the address you assigned the client in Step 7. On a Linux client at /etc/wireguard/wg0.conf:

[Interface]
PrivateKey = <client-private-key>
Address = 10.50.0.2/24

[Peer]
PublicKey = <server-public-key-from-step-5>
Endpoint = <vm-public-ip>:51820
AllowedIPs = 10.50.0.0/24
PersistentKeepalive = 25

Set AllowedIPs = 0.0.0.0/0 instead of 10.50.0.0/24 if you want all client internet traffic to route through the VPN (full-tunnel mode — see Step 11 first to enable NAT on the server).

Bring the client tunnel up:

sudo wg-quick up wg0
sudo wg show

The client and server should now show a latest handshake of 1 second ago and increasing transfer: byte counts.

Step 9: WireGuard Components

Component Path

WireGuard kernel module in-tree on Linux 5.6+

wg userspace tool /usr/bin/wg

wg-quick wrapper /usr/bin/wg-quick

Server config /etc/wireguard/wg0.conf

Systemd unit /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service

Firstboot script /usr/local/sbin/wireguard-firstboot.sh

Firstboot service /etc/systemd/system/wireguard-firstboot.service

Credentials file /stage/scripts/wireguard-credentials.log

Firstboot sentinel /var/lib/cloudimg/wireguard-firstboot.done

IP forwarding sysctl /etc/sysctl.d/99-wireguard.conf

Inspect the installed version:

wg --version

Step 10: Managing the WireGuard Service

Status:

sudo systemctl status wg-quick@wg0.service --no-pager
sudo wg show

Stop / Start / Restart:

sudo systemctl stop wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo systemctl restart wg-quick@wg0

Reload config (no tunnel drop for unchanged peers):

sudo wg-quick strip wg0 | sudo wg syncconf wg0 /dev/stdin

View kernel-side log entries:

sudo journalctl -k --no-pager -n 50 | grep -i wireguard

Check IP forwarding (required for peer-to-peer routing):

cat /proc/sys/net/ipv4/ip_forward

Expected: 1. If it ever resets, sudo sysctl -p /etc/sysctl.d/99-wireguard.conf re-applies it.

Step 11: Enable Full-Tunnel Internet Egress (Optional)

By default the cloudimg image runs as a private peer-to-peer overlay only — peers reach each other but not the wider internet through the VM. To act as an internet gateway (so a client with AllowedIPs = 0.0.0.0/0 routes all its traffic through this VM), uncomment the PostUp/PostDown lines in /etc/wireguard/wg0.conf:

sudo sed -i 's|^#PostUp = iptables|PostUp = iptables|' /etc/wireguard/wg0.conf
sudo sed -i 's|^#PostDown = iptables|PostDown = iptables|' /etc/wireguard/wg0.conf

Confirm eth0 is the real WAN interface (ip route show default); replace if your VM uses a different NIC name. Then restart:

sudo systemctl restart wg-quick@wg0
sudo iptables -t nat -L POSTROUTING -n

Expected: a MASQUERADE rule on the WAN interface. Clients with AllowedIPs = 0.0.0.0/0 now egress through the VM's public Azure IP.

Step 12: Troubleshooting

Peer never gets a handshake

  • Confirm the client PublicKey in wg0.conf exactly matches cat client-pub.key on the client (no trailing newline, no whitespace)

  • Confirm the client Endpoint is the VM's public Azure IP, not the private VNet IP

  • Confirm the NSG allows inbound UDP 51820 from the client's source IP

  • Confirm UDP is not blocked at the client's network egress (some captive portals block all UDP except 53/443)

  • sudo tcpdump -ni eth0 udp port 51820 on the server while the client tries to connect — you should see inbound packets

Handshake succeeds but no traffic flows

  • Confirm IP forwarding: cat /proc/sys/net/ipv4/ip_forward should be 1

  • Confirm the client's AllowedIPs covers the destination — 10.50.0.0/24 for overlay, 0.0.0.0/0 for full-tunnel

  • For full-tunnel, confirm Step 11 NAT rules are active: sudo iptables -t nat -L POSTROUTING -n

wg-quick up wg0 fails with "RTNETLINK: File exists"

  • The interface is already up: sudo wg show wg0 to confirm, sudo wg-quick down wg0 && sudo wg-quick up wg0 to reset

Lost the server private key (somehow)

  • The private key cannot be recovered. Generate a new server keypair (wg genkey | tee /etc/wireguard/server-priv.key | wg pubkey), update the PrivateKey in /etc/wireguard/wg0.conf, restart wg-quick@wg0, and distribute the new server public key to every peer (each peer must update its [Peer] PublicKey = line)

Step 13: Security Recommendations

  • Treat the VM like a bastion. The WireGuard server's private key in /etc/wireguard/wg0.conf (mode 0600 root) is the master credential — if attackers root the VM, they impersonate the entire VPN

  • Generate client keys on the client, never on the server. The server should only ever see client public keys

  • Restrict each peer's AllowedIPs to the smallest CIDR you actually need on the server side — 10.50.0.2/32 for a single peer is far safer than 10.50.0.0/24, which would let one compromised peer impersonate any other

  • Use PersistentKeepalive = 25 on clients behind NAT to keep the UDP NAT mapping alive

  • Rotate keys if a peer device is lost or compromised — remove the [Peer] block from the server wg0.conf and reload

  • Monitor wg show for stale peers — peers with no recent handshake are inactive and should be removed

  • Pair with Azure NSG flow logs if you need audit trail on UDP 51820 source IPs

Step 14: Support and Licensing

WireGuard is licensed under the GPLv2 (kernel module) and the MIT/GPLv2 dual license (userspace wg/wg-quick tools). There is no per-peer, per-tunnel, or per-server fee for any WireGuard component or for the upstream protocol.

cloudimg provides commercial support for this image separately from the upstream project.

  • Email: support@cloudimg.co.uk

  • Website: www.cloudimg.co.uk

  • Support hours: 24/7 with guaranteed 24 hour response SLA

Deploy on Azure

Launch WireGuard on Ubuntu 24.04 with 24/7 support from cloudimg.

View on Marketplace

Need Help?

Our support team is available 24/7.

support@cloudimg.co.uk