wg-easy
Videos
Overview: wg-easy WireGuard VPN on Ubuntu 24.04 LTS on Azure: overview (watch on YouTube)
Launch and log in: wg-easy WireGuard VPN on Ubuntu 24.04 LTS on Azure: launch and log in (watch on YouTube)
Deep Dive: wg-easy WireGuard VPN on Ubuntu 24.04 LTS on Azure: deep dive (watch on YouTube)
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 use a clean web console to add clients, hand out configuration files and QR codes, enable or disable peers, and watch live connection status.
The SecureAnyCloud 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. 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 that wg-easy uses for live peer status.
wg-easy's configuration and peer database live on a dedicated Azure data disk. A unique admin password and the public WireGuard endpoint are generated on the first boot of every VM.
What you get:
- 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, with 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-easythat holds the WireGuard config and peer database docker.serviceandnginx.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - 24/7 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point.
Configure the network security group (NSG) to allow inbound 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), because 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 SecureAnyCloud, and select Create.
On the Basics tab, 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). You add the WireGuard UDP 51820 rule after creation, in Step 2.
Review the dedicated data disk on the Disks tab, then select Review + create and Create.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:wg-easy-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name wg-easy \
--image sac1647283583153:wg-easy-ubuntu-24-04:default:latest \
--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 1020WireGuard is a UDP protocol. Make sure the 51820/udp rule reaches the VM. Without it, the web UI works but 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
sudo docker exec wg-easy wg showBoth services report active. Docker manages the wg-easy container, and 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
First boot generates a unique admin password for your VM and writes it to a root-only file:
sudo cat /root/wg-easy-credentials.txtThis file contains WG_EASY_USERNAME (always admin), WG_EASY_PASSWORD, the WG_EASY_URL to open, and WIREGUARD_ENDPOINT. WIREGUARD_ENDPOINT is the public address that clients dial, auto-detected on first boot.
If your VM sits behind a NAT gateway or you use a DNS name, correct the endpoint 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. Request it with:
curl -s http://localhost/healthzThe endpoint returns ok. It never requires authentication, so it is safe to use 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, and the per-VM password returns 200.
The command embeds your unique password, so 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.
After you sign in, the Clients dashboard opens. A fresh VM has no clients yet, so you add your first one in the next step.
Step 9 - Add a VPN client
Select New (or New Client) and give the client a name such as my-laptop. Optionally set an expiry date, then select Create Client.
The client appears in the list with its assigned VPN address. From the client 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
Select 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 the camera at the screen. On a desktop, select the download icon to save the .conf file, then 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 stores its WireGuard configuration, keys and peer database under /var/lib/wg-easy on the dedicated Azure data disk. The directory is bind-mounted into the container as /etc/wireguard, so the data survives OS changes and you can resize the disk independently. Confirm the mount:
findmnt /var/lib/wg-easyA separate Azure data disk backs the mount. The disk is 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 the endpoint in the web UI under Admin panel -> Host and re-issue the client configs. - Upgrades.
wg-easyruns as the wg-easy container 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 that 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, and 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 (for example, certbot with your own domain) before you run it in production, because 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
SecureAnyCloud provides 24/7 expert support for this image. Contact sales@secureanycloud.com.
Deploy on Azure
Launch wg-easy 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
This image runs wg-easy, the easiest way to run your own WireGuard VPN server and manage it entirely from your web browser. WireGuard is a modern, fast and secure VPN protocol. wg-easy wraps it in a clean admin console where you add and remove peers, download ready-to-use client configuration files and QR codes, and see each client's connection status at a glance.
wg-easy runs as a Docker container with the network capabilities (NET_ADMIN, SYS_MODULE) and IP-forwarding settings that a VPN gateway needs. The encrypted WireGuard tunnel is published on UDP port 51820. The web admin UI binds to 127.0.0.1:51821 on the loopback interface and is fronted by nginx as a reverse proxy on port 80 (and 443 once you add TLS), with the WebSocket upgrade that the live interface uses already configured.
wg-easy's database of the admin user, the WireGuard interface keys, peers and settings lives on a dedicated EBS data volume mounted at /var/lib/wg-easy. The volume is separate from the operating system disk, and you can resize it independently.
On the first boot of every deployed instance, a one-shot service creates the wg-easy admin account with a per-instance password. The service also completes the first-run setup with the instance's own public address, so generated client configurations point at the right endpoint. It records the password in /root/wg-easy-credentials.txt with mode 0600. No shared or default credentials ship in the image. The release in this image is wg-easy 15.3.0.
Prerequisites
Before you deploy this image, you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group that allows inbound TCP port 22 from your management network, inbound TCP port 80 from the networks your operators use to reach the admin UI, and inbound UDP port 51820 from anywhere your VPN clients connect from
- The AWS CLI (version 2) installed locally, if you plan to deploy from the command line
Launch the Instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for wg-easy. Select the SecureAnyCloud listing and choose Select, then Continue on the subscription summary.
Pick an instance type of t3.small or larger. Choose your EC2 key pair under Key pair (login).
Under Network settings, select your VPC and subnet. Create or select a security group that allows inbound TCP port 22 from your management network, inbound TCP port 80 from the networks your operators use, and inbound UDP port 51820 from the networks your VPN clients connect from.
Leave the root volume at the default size or larger. The image adds a separate data volume for the wg-easy database automatically.
Select Launch instance. First-boot initialization takes under a minute after the instance state becomes Running and the status checks pass.
Launch the Instance from the AWS CLI
The following block launches an instance from the SecureAnyCloud wg-easy Marketplace AMI into an existing subnet and security group. Substitute your own values for the following:
- Replace <ami-id> with the AMI ID shown on the Marketplace listing.
- Replace <key-name> with your EC2 key pair name.
- Replace <subnet-id> with your subnet ID.
- Replace <security-group-id> with a security group that opens TCP 22, TCP 80 and UDP 51820 as described above.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type t3.small \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":20,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wg-easy-01}]'On success, the command prints a JSON document. Note the instance ID. Once the instance is running, retrieve its public address with aws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.
Connect and Retrieve the Admin Password
Connect over SSH with the key pair you selected and the public IP address from Step 2. The SSH login user depends on the operating system of the AMI variant you launched:
- AMI variant wg-easy 15 on Ubuntu 24.04: SSH login user ubuntu
The first-boot service runs before the SSH daemon becomes ready, so the credentials file is always in place when you log in for the first time. Print it:
ssh <login-user>@<public-ip>
sudo cat /root/wg-easy-credentials.txtThe plain text file contains the web UI URL, the admin username, the per-instance admin password and the WireGuard tunnel endpoint.
From the same SSH session, confirm that the deployment is healthy. All three services are active, the container is healthy, and the WireGuard interface wg0 is listening on UDP 51820:
systemctl is-active docker.service nginx.service wg-easy-firstboot.service
sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
sudo docker exec wg-easy wg showactive active active NAMES IMAGE STATUS PORTS wg-easy ghcr.io/wg-easy/wg-easy:15.3.0 Up 3 minutes (healthy) 0.0.0.0:51820->51820/udp, [::]:51820->51820/udp, 127.0.0.1:51821->51821/tcp interface: wg0 public key: W7QaZaccMZ00Retasuxzc55prnBa6tscp0E6AcSPzk0= private key: (hidden) listening port: 51820nginx serves an unauthenticated health endpoint for load-balancer and uptime checks:
curl http://<public-ip>/healthzokSign In to the Web UI
Open http://<public-ip>/ in your browser. The wg-easy sign-in page appears. Enter the username admin and the password from /root/wg-easy-credentials.txt.
After you sign in, you land on the Clients dashboard. A fresh instance has no clients yet. Every WireGuard peer you add appears here with its tunnel addresses and controls to enable, disable, edit or remove it.
Add a VPN Client
Select New Client (or the New button), give the client a name such as my-laptop, and optionally set an expiry date. Select Create Client.
The new client appears in the list immediately, with its own tunnel addresses assigned (for example 10.8.0.2). wg-easy generates the client's key pair and a complete WireGuard configuration that points at this instance's public address automatically, so there is nothing to edit by hand.
Connect a Device
Each client row offers two ways to onboard a device. To use a phone, select the QR code icon to display a QR code, and scan it straight into the WireGuard mobile app on iOS or Android. The tunnel is up in seconds.
For a desktop, select the download icon on the client row to download that client's .conf file. Then import the file into the WireGuard client for Windows, macOS or Linux.
Once a device connects, confirm the peer's handshake and transfer from the same SSH session:
sudo docker exec wg-easy wg showWireGuard is connectionless, so a client appears in the peer list only after it sends its first packet through the tunnel. Make sure that inbound UDP 51820 is open in the instance security group from wherever your clients connect.
Configure HTTPS (Recommended)
The admin UI is served over plain HTTP on port 80 out of the box. This is fine behind a load balancer that terminates TLS, or over a trusted management network. For direct internet exposure, put the admin UI behind HTTPS.
Point a DNS name at the instance, open port 443 in the security group, install certbot, and obtain a certificate:
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/local/bin/certbot
sudo certbot --nginx -d your-wg-easy-host.example.comcertbot edits the nginx site in place to add the TLS listener and the redirect from port 80.
WireGuard itself is always encrypted end to end, regardless of the admin UI's TLS. HTTPS here protects the admin console, not the VPN tunnel.
Backup and Maintenance
wg-easy stores everything in its SQLite database on the data volume mounted at /var/lib/wg-easy. This includes the admin user, the WireGuard server keys, every peer and all settings. Back up that directory to preserve your VPN configuration:
sudo tar czf <backup-dir>/wg-easy-$(date +%F).tgz -C /var/lib/wg-easy .The container runs with --restart=always, so it comes back automatically after a reboot.
To update wg-easy, pull a newer pinned image tag and recreate the container against the same data volume. The database persists on /var/lib/wg-easy, so your peers and keys survive the upgrade.
Support
SecureAnyCloud provides 24/7 technical support for this image. Support covers deployment, DNS and public-endpoint configuration, HTTPS and reverse-proxy setup, client onboarding, split-tunnel and allowed-IP configuration, and storage and upgrade planning.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
Deploy on AWS
Launch wg-easy on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com