wg-easy WireGuard VPN on Ubuntu 24.04 on Azure User Guide
Overview
wg-easy is the easiest way to run your own WireGuard VPN and manage it from a browser. Instead of hand-editing wg0.conf and generating keys on the command line, you add clients, hand out configuration files and QR codes, enable or disable peers, and watch live connection status from a clean web console. The cloudimg image installs Docker CE from the official Docker repository and runs wg-easy 15.3.0 as a container (managed by Docker with a restart policy). The WireGuard tunnel listens on UDP 51820, while the web admin UI is bound to the loopback connector 127.0.0.1:51821 behind an nginx reverse proxy on port 80, with the WebSocket upgrade wg-easy uses for live peer status. wg-easy's configuration and peer database live on a dedicated Azure data disk, and a unique admin password plus the public WireGuard endpoint are generated on the first boot of every VM. Backed by 24/7 cloudimg support.
What is included:
- wg-easy 15.3.0 running as a container managed by Docker
- Docker CE preinstalled from the official Docker apt repository
- A WireGuard VPN tunnel on UDP
51820, the address auto-detected on first boot - The web admin UI on
:80, fronted by nginx with the WebSocket upgrade proxied - A unique admin password generated on first boot and recorded in a root-only file (username
admin) - A dedicated Azure data disk at
/var/lib/wg-easyholding the WireGuard config and peer database docker.service+nginx.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point. NSG inbound rules: allow 22/tcp from your management network, 80/tcp for the admin UI, and 51820/udp for the WireGuard tunnel itself - without the UDP rule clients cannot connect. The admin UI serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain (see Maintenance), since the console can add and remove VPN access to your network.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for wg-easy by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). The WireGuard UDP 51820 rule is added after creation in Step 2. Review the dedicated data disk on the Disks tab, then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name wg-easy \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
# Open the admin UI (TCP 80) and the WireGuard tunnel (UDP 51820)
az vm open-port --resource-group <your-rg> --name wg-easy --port 80 --priority 1010
az vm open-port --resource-group <your-rg> --name wg-easy --port 51820 --priority 1020
WireGuard is a UDP protocol - make sure the 51820/udp rule reaches the VM, or the web UI will work while no client can establish a tunnel.
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services and tunnel are up
systemctl is-active docker.service nginx.service
docker exec wg-easy wg show
Both services report active. Docker manages the wg-easy container; wg show reports the wg0 interface listening on port 51820. wg-easy's configuration and peer database live on the dedicated Azure data disk mounted at /var/lib/wg-easy.

Step 5 - Retrieve your admin password and endpoint
The admin password is generated uniquely on the first boot of your VM and written to a root-only file:
sudo cat /root/wg-easy-credentials.txt
This file contains WG_EASY_USERNAME (always admin), WG_EASY_PASSWORD, the WG_EASY_URL to open, and WIREGUARD_ENDPOINT - the public address clients will dial, auto-detected on first boot. If your VM sits behind a NAT gateway or you use a DNS name, correct it later in the web UI under Admin panel -> Host.

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Verify authentication from the command line
wg-easy's API uses HTTP Basic authentication with the same credentials as the web UI. An unauthenticated request or a wrong password returns 401; the per-VM password returns 200. Because the command embeds your unique password, run it interactively rather than from a script - substitute the value of WG_EASY_PASSWORD from Step 5 for <WG_EASY_PASSWORD>:
curl -s -o /dev/null -w '%{http_code}\n' -u admin:<WG_EASY_PASSWORD> http://127.0.0.1/api/client
A correct password prints 200; an unauthenticated request or a wrong password prints 401.

Step 8 - Sign in to the console
Browse to http://<vm-public-ip>/. wg-easy shows a sign-in page; enter the username admin and the password from Step 5.

Once signed in, the Clients dashboard is shown. On a fresh VM there are no clients yet - you add your first one next.

Step 9 - Add a VPN client
Click New (or New Client), give the client a name such as my-laptop, optionally set an expiry date, and click Create Client.

The client appears in the list with its assigned VPN address. From the row you can enable or disable the peer, edit it, show its QR code, download its .conf file, or generate a one-time share link.

Step 10 - Connect a device
Click the QR code icon to display the client configuration. On a phone, open the official WireGuard app, choose Add tunnel -> Scan from QR code, and point it at the screen. On a desktop, use the download icon to save the .conf file and import it into the WireGuard client. The tunnel connects to the WIREGUARD_ENDPOINT from Step 5 over UDP 51820.

Step 11 - Confirm data lives on the dedicated disk
wg-easy's WireGuard configuration, keys and peer database are stored under /var/lib/wg-easy on the dedicated Azure data disk (bind-mounted into the container as /etc/wireguard), so they survive OS changes and can be resized independently:
findmnt /var/lib/wg-easy
The mount is backed by a separate Azure data disk captured into the image and re-provisioned on every VM.
Maintenance
- Password: the admin password is set on first boot. To change it, sign in and use the account menu in the console.
- Public endpoint: clients dial the
WIREGUARD_ENDPOINTrecorded on first boot. If your VM is behind a NAT gateway or you move to a DNS name, update it in the web UI under Admin panel -> Host and re-issue client configs. - Upgrades: wg-easy runs as the
wg-easycontainer from the pinnedghcr.io/wg-easy/wg-easy:15.3.0image; to upgrade, pull a newer tag, remove the container and re-run it with the same-v /var/lib/wg-easy:/etc/wireguardmount so your configuration is preserved. - Storage: all wg-easy state lives under
/var/lib/wg-easyon the data disk; back up that volume to protect your keys and peer list. - Firewall: the WireGuard tunnel needs
51820/udpopen to the VM; the admin UI needs80/tcp. Restrict80/tcpto trusted networks where possible. - TLS: the admin UI serves plain HTTP on port 80; front it with TLS (e.g. certbot with your own domain) before production use, since the console can grant and revoke VPN access to your network.
- Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.