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
wgandwg-quickuserspace 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.confand startswg-quick@wg0.- A default config at
/etc/wireguard/wg0.confwith the server interface on10.50.0.1/24, listening on UDP 51820, and an example[Peer]block commented in. - The
wg-quick@wg0.servicesystemd 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-toolspackage 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.
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.
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 StandardConnect 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.
Verify the WireGuard service
Check the service status:
sudo systemctl status wg-quick@wg0.service --no-pagerThe 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_DONEConfirm the interface and listener:
ip -brief addr show wg0
sudo wg showThe 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.
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.logTo 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.
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.keyOn 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.
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 PEERReload the interface to pick up the new peer without dropping existing tunnels:
sudo wg-quick strip wg0 | sudo wg syncconf wg0 /dev/stdinOr restart the service, which briefly drops other peers:
sudo systemctl restart wg-quick@wg0Confirm that the new peer appears:
sudo wg showConfigure 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 = 25To 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 showThe client and the server should now show a latest handshake of 1 second ago and increasing transfer: byte counts.
Review the WireGuard components
The image installs these components:
- WireGuard kernel module: in-tree on Linux 5.6+
wguserspace tool: /usr/bin/wgwg-quickwrapper: /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 --versionManage the WireGuard service
Check status:
sudo systemctl status wg-quick@wg0.service --no-pager sudo wg showStop, start or restart the service:
sudo systemctl stop wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo systemctl restart wg-quick@wg0Reload config (no tunnel drop for unchanged peers):
sudo wg-quick strip wg0 | sudo wg syncconf wg0 /dev/stdinView kernel-side log entries:
sudo journalctl -k --no-pager -n 50 | grep -i wireguardCheck IP forwarding (required for peer-to-peer routing):
cat /proc/sys/net/ipv4/ip_forwardThe expected value is 1. If it ever resets, sudo sysctl -p /etc/sysctl.d/99-wireguard.conf re-applies it.
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.confConfirm 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 -nThe 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.
Troubleshooting
Peer never gets a handshake
- Confirm that the client
PublicKeyinwg0.confexactly matchescat client-pub.keyon the client, with no trailing newline and no whitespace. - Confirm that the client
Endpointis 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 51820on 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_forwardshould print1. - Confirm that the client's
AllowedIPscovers the destination:10.50.0.0/24for the overlay, or0.0.0.0/0for 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 wg0to confirm, andsudo wg-quick down wg0 && sudo wg-quick up wg0to 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 thePrivateKeyin/etc/wireguard/wg0.confand restartwg-quick@wg0. Then distribute the new server public key to every peer. Each peer must update its[Peer] PublicKey =line.
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
AllowedIPsto the smallest CIDR you need on the server side.10.50.0.2/32for a single peer is far safer than10.50.0.0/24, which would let one compromised peer impersonate any other. - Use
PersistentKeepalive = 25on 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 serverwg0.confand reload. - Monitor
wg showfor 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.
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
Overview
WireGuard is a fast, modern and lean VPN that lives in the Linux kernel. It uses Curve25519 for key exchange and ChaCha20-Poly1305 for data encryption. It presents a single UDP listener with a small, stateless configuration file, with no PKI, no certificate authority and no IKE daemon.
This SecureAnyCloud image runs WireGuard on AWS. It installs WireGuard from the official distribution repository. The kernel module is in-tree, so there is no DKMS build and no third-party repository. WireGuard starts automatically under systemd, so an encrypted tunnel endpoint is listening on UDP 51820 within minutes of launch.
No baked secret. There is no server private key inside the image. A fresh Curve25519 server keypair is generated on this instance's first boot and written only into the root-only server configuration /etc/wireguard/wg0.conf (mode 0600). It is never captured into the image, so every instance you launch has its own unique server identity. The server public key and connection details are written to a root-only file for you to hand to your client devices.
Server-only by default. The appliance ships with zero peers configured, so it is a private server-only endpoint rather than an open relay. You add one [Peer] block per client device that you explicitly authorize. IP forwarding is enabled so the instance can route between connected peers.
What you get:
- WireGuard preinstalled and run under systemd (
wg-quick@wg0.service), listening on UDP 51820. - A per-instance Curve25519 server keypair, generated on first boot and never baked into the image.
- A default
10.50.0.0/24overlay (the server is10.50.0.1), with IP forwarding enabled. - A server-only default configuration (no peers), with an example peer block and optional NAT gateway rules ready to uncomment.
- Unattended security upgrades left enabled, so the server keeps receiving patches.
Prerequisites
- An active AWS account, an EC2 key pair, and a VPC and subnet in the target region.
- A subscription to the WireGuard listing on AWS Marketplace.
- A security group that allows TCP 22 (admin) and, from the clients you intend to serve, UDP 51820 (WireGuard).
Recommended instance type: m5.large (2 vCPU, 8 GB RAM). WireGuard is extremely lightweight. A busy gateway with many peers can move to a larger instance type.
Launch from AWS Marketplace
Find WireGuard on AWS Marketplace, published by SecureAnyCloud. Choose Continue to Subscribe, then Continue to Configuration and Continue to Launch.
Select the Ubuntu 24.04 delivery option, your instance type (m5.large) and your key pair. Choose a security group that allows TCP 22 for administration plus UDP 51820 from the client networks you want to serve.
Connecting to your instance
Connect over SSH with your EC2 key pair. For Ubuntu 24.04, the login user is ubuntu:
ssh -i your-key.pem ubuntu@<public-ip>First boot
On first boot, the image generates a per-instance Curve25519 server keypair and writes /etc/wireguard/wg0.conf (mode 0600, root only). It resolves the instance public IP through EC2 instance metadata for the connection note, and starts wg-quick@wg0. This completes within a minute.
Confirm that the tunnel is active and listening:
sudo systemctl start wg-quick@wg0 2>/dev/null || true sleep 1
sudo systemctl is-active wg-quick@wg0
sudo wg show wg0 ip -brief addr show wg0The service reports active. wg show reports the listening port 51820 and this instance's server public key, and the wg0 interface carries 10.50.0.1/24.
Retrieve your server public key and connection details
The per-instance server public key and connection details live in a root-only file. The server private key never leaves /etc/wireguard/wg0.conf, and this file does not show it.
sudo cat /stage/scripts/wireguard-credentials.logThe file holds SERVER_PUBLIC_KEY, the SERVER_ENDPOINT (this instance's public IP and UDP 51820), the server VPN address (10.50.0.1) and the overlay subnet (10.50.0.0/24). These values are unique to this instance. First boot generates them, and they are never baked into the image.
Authorize a client device
On the client device (your laptop, phone or another server), generate a keypair. On Linux, run:
wg genkey | tee client-privkey | wg pubkey > client-pubkeySend the client public key to the server. On the server, add a [Peer] block to /etc/wireguard/wg0.conf, one per device, with a unique AllowedIPs address in the overlay:
[Peer] PublicKey = <client-public-key> AllowedIPs = 10.50.0.2/32Apply the change:
sudo systemctl reload wg-quick@wg0Then, on the client, write a WireGuard configuration that points at this server. Use the SERVER_PUBLIC_KEY and SERVER_ENDPOINT from Step 4:
[Interface] PrivateKey = <client-private-key> Address = 10.50.0.2/24 [Peer] PublicKey = <server-public-key> Endpoint = <server-public-ip>:51820 AllowedIPs = 10.50.0.0/24 PersistentKeepalive = 25Bring the client tunnel up. On Linux, run wg-quick up <config>. On macOS, Windows, iOS or Android, import the file into the WireGuard app. To route all of the client's traffic through the VPN, set AllowedIPs = 0.0.0.0/0 on the client instead.
Confirm a peer has connected
Once a client connects, wg show lists the peer with a recent handshake and transfer counters. This proves a real encrypted tunnel, not just a bound listener:
sudo wg show wg0A connected peer appears under the interface with its public key, latest handshake a few seconds ago, and non-zero transfer figures.
Optional - route client internet traffic through the VPN
By default WireGuard runs as a private peer-to-peer overlay. Peers can reach each other and the server, but not the wider internet through the server. To make the instance an internet gateway for your clients, uncomment the two NAT lines in /etc/wireguard/wg0.conf. Replace eth0 with the real WAN interface from ip route show default:
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADERestart the tunnel with sudo systemctl restart wg-quick@wg0, then set AllowedIPs = 0.0.0.0/0 on the client. The client's traffic now egresses through the instance. IP forwarding is already enabled in the image.
Security and configuration notes
Per-instance server key. The Curve25519 server private key is generated on first boot and lives only in /etc/wireguard/wg0.conf (0600 root:root). It is never baked into the image, so no two customer instances share a server identity.
Server-only by default. The image ships with no peers. It is not an open endpoint, because a device can connect only once you add its public key as a [Peer].
The overlay is 10.50.0.0/24 with the server at 10.50.0.1. Give each client device a unique /32 address (10.50.0.2, 10.50.0.3, ...).
Firewall. In the instance security group, open UDP 51820 to the client networks you want to serve. Keep the rules as tight as your use case allows.
Managing the tunnel. After you edit the config, reload with sudo systemctl reload wg-quick@wg0, or restart with sudo systemctl restart wg-quick@wg0. The service is enabled at boot.
Managing the service
Check the service status:
sudo systemctl status wg-quick@wg0 --no-pagerYou manage the tunnel entirely through systemd and the wg CLI. There is no web interface. You configure WireGuard through the wg0.conf file and the wg/wg-quick tools.
Support
SecureAnyCloud publishes and supports this image. For assistance, contact SecureAnyCloud support through the AWS Marketplace listing. WireGuard is distributed under the GPL-2.0 license. WireGuard is a registered trademark of Jason A. Donenfeld; SecureAnyCloud is not affiliated with or endorsed by the WireGuard project or Jason A. Donenfeld.
Deploy on AWS
Launch WireGuard VPN Server with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com