We
Networking Azure

wg-easy WireGuard VPN on Ubuntu 24.04 on Azure User Guide

| Product: wg-easy WireGuard VPN on Ubuntu 24.04 LTS on Azure

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-easy holding the WireGuard config and peer database
  • docker.service + nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint 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.

docker.service, nginx.service and wg-easy-firstboot all active, the wg-easy container healthy with UDP 51820 published, the WireGuard wg0 interface listening on port 51820, and the dedicated 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.

The Docker and wg-easy versions and the per-VM credentials file with the generated admin password, web URL and the auto-detected public WireGuard endpoint

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.

The nginx health endpoint returning ok and the admin auth round-trip: unauthenticated and wrong-password requests rejected with 401, and the per-VM password accepted with 200

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.

The wg-easy WireGuard sign-in page in the browser

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

The wg-easy Clients dashboard on a fresh VM, showing no clients yet and a New Client button

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 New Client dialog with a client name entered and an optional expiry date field

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.

The Clients list showing the newly created my-laptop peer with its assigned address 10.8.0.2 and per-client actions: enable toggle, edit, QR code, download configuration, and 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.

The client configuration QR code dialog for the my-laptop peer, ready to scan into the WireGuard mobile app, with copy and download PNG options

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_ENDPOINT recorded 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-easy container from the pinned ghcr.io/wg-easy/wg-easy:15.3.0 image; to upgrade, pull a newer tag, remove the container and re-run it with the same -v /var/lib/wg-easy:/etc/wireguard mount so your configuration is preserved.
  • Storage: all wg-easy state lives under /var/lib/wg-easy on the data disk; back up that volume to protect your keys and peer list.
  • Firewall: the WireGuard tunnel needs 51820/udp open to the VM; the admin UI needs 80/tcp. Restrict 80/tcp to 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.