Deployment guide

WireGuard

Videos

Overview: WireGuard VPN Ubuntu 24.04 on Azure: overview (watch on YouTube)

Launch and log in: WireGuard VPN Ubuntu 24.04 on Azure: launch and log in (watch on YouTube)

Deep Dive: WireGuard VPN Ubuntu 24.04 on Azure: deep dive (watch on YouTube)

Overview

WireGuard is a modern, fast, audited VPN protocol that lives in the Linux kernel from 5.6 onwards. It uses Curve25519 for key exchange and ChaCha20Poly1305 for data encryption. It presents a single UDP listener with a stateless config file, with no PKI, no IKE/IPsec daemon and no certificates.

This SecureAnyCloud image runs WireGuard on Ubuntu 24.04 on Azure. It 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 and no third-party PPA.

At first boot, wireguard-firstboot.service generates a fresh per-VM Curve25519 server keypair and writes /etc/wireguard/wg0.conf (mode 0600, root only). It then 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, with no peer blocks pre-configured. You 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 you get:

  • WireGuard kernel module and the wg and wg-quick userspace tools from the Ubuntu noble main repo.
  • wireguard-firstboot.service, a 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.
  • A 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.
  • The wg-quick@wg0.service systemd unit, which starts automatically on boot.
  • IP forwarding (net.ipv4.ip_forward=1, net.ipv6.conf.all.forwarding=1) enabled at install time through /etc/sysctl.d/99-wireguard.conf.
  • A server public key and endpoint info file at /stage/scripts/wireguard-credentials.log (mode 0600, root only). Share the public key with peer clients, and never copy the private key.
  • Ubuntu 24.04 LTS base with the latest security patches applied at build time.
  • Azure Linux Agent for cloud integration and SSH key injection.
  • 24/7 SecureAnyCloud 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.
  • WireGuard already installed on each peer client device: the Linux kernel module, the official WireGuard app on macOS, iOS, Windows or Android, or the wireguard-tools package on a peer Linux machine.

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM). WireGuard is extremely lightweight, and 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 more than 50 active peers should move to Standard_D2s_v3 or larger. A larger size gives consistent CPU for crypto and allows accelerated networking on the NIC.

1

Deploy from the Azure Portal

In the Azure Portal, open Marketplace, search for WireGuard, select the SecureAnyCloud publisher entry and choose 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. If peers connect from the open internet, you can use 0.0.0.0/0, because 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. You may still prefer to scope the rule to known client networks.

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

2

Deploy from the Azure CLI

Run these commands on your own workstation. They create the network, a network security group with SSH and WireGuard rules, and the VM:

RG="wireguard-prod" LOCATION="eastus" VM_NAME="wireguard-01" ADMIN_USER="azureuser" IMAGE_URN="sac1647283583153:wireguard-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn "$IMAGE_URN"
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 "$IMAGE_URN" \
  --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
3

Connect via SSH

Connect to the VM as the administrator user:

ssh azureuser@<vm-ip>

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

4

Verify the WireGuard service

Check the service status:

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

The expected state is active (exited). wg-quick is a oneshot that brings the interface up and exits. Confirm the first-boot sentinel:

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

Confirm the interface and listener:

ip -brief addr show wg0
sudo wg show

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

5

Read the server public key

First boot writes the server public key to a root-only file with mode 0600. Read the file with:

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

To print only the key, run:

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

Share this public key, together 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.

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/. Choose Add Tunnel, then 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.

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, which 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 the service, which briefly drops other peers:

sudo systemctl restart wg-quick@wg0

Confirm that the new peer appears:

sudo wg show
8

Configure the client

Build the client config from 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, save it 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

To route all client internet traffic through the VPN (full-tunnel mode), set AllowedIPs = 0.0.0.0/0 instead of 10.50.0.0/24. Enable NAT on the server first (Step 11).

Bring the client tunnel up:

sudo wg-quick up wg0
sudo wg show

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

9

Review the WireGuard components

The image installs these components:

  • 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
  • First-boot script: /usr/local/sbin/wireguard-firstboot.sh
  • First-boot service: /etc/systemd/system/wireguard-firstboot.service
  • Credentials file: /stage/scripts/wireguard-credentials.log
  • First-boot sentinel: /var/lib/sac/data/wireguard-firstboot.done
  • IP forwarding sysctl: /etc/sysctl.d/99-wireguard.conf

Inspect the installed version:

wg --version
10

Manage the WireGuard service

Check status:

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

Stop, start or restart the service:

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

The expected value is 1. If it ever resets, sudo sysctl -p /etc/sysctl.d/99-wireguard.conf re-applies it.

11

Enable full-tunnel internet egress (optional)

By default the SecureAnyCloud image runs as a private peer-to-peer overlay only. Peers reach each other, but not the wider internet through the VM. To make the VM an internet gateway, so that a client with AllowedIPs = 0.0.0.0/0 routes all its traffic through it, uncomment the PostUp and 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 that eth0 is the real WAN interface by running ip route show default. Replace it if your VM uses a different NIC name. Then restart the service:

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

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

12

Troubleshooting

Peer never gets a handshake

  • Confirm that the client PublicKey in wg0.conf exactly matches cat client-pub.key on the client, with no trailing newline and no whitespace.
  • Confirm that the client Endpoint is the VM's public Azure IP, not the private VNet IP.
  • Confirm that the NSG allows inbound UDP 51820 from the client's source IP.
  • Confirm that UDP is not blocked at the client's network egress. Some captive portals block all UDP except 53 and 443.
  • Run 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 print 1.
  • Confirm that the client's AllowedIPs covers the destination: 10.50.0.0/24 for the overlay, or 0.0.0.0/0 for full-tunnel.
  • For full-tunnel, confirm that the Step 11 NAT rules are active with sudo iptables -t nat -L POSTROUTING -n.

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

  • The interface is already up. Run sudo wg show wg0 to confirm, and sudo wg-quick down wg0 && sudo wg-quick up wg0 to reset it.

Lost the server private key

  • You cannot recover the private key. Generate a new server keypair (wg genkey | tee /etc/wireguard/server-priv.key | wg pubkey), update the PrivateKey in /etc/wireguard/wg0.conf and restart wg-quick@wg0. Then distribute the new server public key to every peer. Each peer must update its [Peer] PublicKey = line.
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 an attacker gains root on the VM, they can 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 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 an audit trail of UDP 51820 source IPs.
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.

SecureAnyCloud provides commercial support for this image, separate from the upstream project.

  • Email: sales@secureanycloud.com
  • Website: secureanycloud.com
  • Support hours: 24/7 with guaranteed 24 hour response SLA

Deploy on Azure

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

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure

Launch WireGuard VPN on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure