Skip to content

Set up a WireGuard VPN with wg-easy and Docker on a VPS

Manage your own WireGuard VPN through a browser, add a separate profile for each device, and keep the admin panel behind an SSH tunnel.

An easier WireGuard setup for Ubuntu and Debian

wg-easy adds a browser interface to WireGuard: create a device, download its configuration, or display a QR code. This tutorial builds a personal IPv4 VPN on a fresh Ubuntu 24.04 LTS or Debian 12/13 VPS. Your Windows, Mac, iPhone, or Android device connects to the Linux server; the server does not need to run your device’s operating system.

  • Use an x86_64 or ARM64 VPS with a public IPv4 address, sudo access, and a kernel that supports WireGuard.
  • Confirm that the plan permits VPN use and has enough transfer allowance for your devices.
  • Keep your working SSH session open when changing firewall rules. Start with a separate VPS if the existing server already runs production applications.

1. Install Docker Engine and the Compose plugin

Use Docker’s official repository instructions for your distribution, including the docker-compose-plugin package. Use a current Docker Engine release; this localhost-binding approach requires version 28 or newer because older releases have a documented same-network exposure issue. These commands use regular, rootful Docker through sudo.

Run on the VPS after installing Docker.
sudo apt update
sudo apt install nano
sudo docker version
sudo docker compose version
sudo install -d -m 0755 /opt/wg-easy
sudo install -d -m 0700 /opt/wg-easy/data
cd /opt/wg-easy
sudo nano compose.yaml

2. Add the wg-easy Docker Compose configuration

Paste the following into compose.yaml. The :15 image tag follows supported v15 updates; do not substitute :latest or copy older PASSWORD and WG_HOST examples. v15 creates the administrator and VPN endpoint through its setup screen.

A persistent IPv4 deployment with a localhost-only admin port.
services:
  vpn:
    image: ghcr.io/wg-easy/wg-easy:15
    restart: unless-stopped
    environment:
      INSECURE: "true"
      DISABLE_IPV6: "true"
    ports:
      - "51820:51820/udp"
      - "127.0.0.1:51821:51821/tcp"
    volumes:
      - ./data:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      net.ipv4.ip_forward: "1"
      net.ipv4.conf.all.src_valid_mark: "1"

INSECURE permits HTTP for the local admin connection; the SSH tunnel in the next step encrypts its journey across the internet. Never change the admin mapping to a public address with this setting. For public browser administration, follow wg-easy’s HTTPS reverse-proxy documentation instead.

sudo docker compose config --quiet
sudo docker compose up -d
sudo docker compose ps

3. Open the VPN port and securely reach the admin panel

TrafficRequired access
UDP 51820Allow inbound to the VPS for WireGuard clients.
TCP 51821Keep closed publicly; use localhost through SSH.
Your SSH TCP portKeep available to your administration IP.

Apply the rules in your provider firewall and account for your host’s Docker firewall rules. Docker-published ports can bypass ordinary UFW rules, so UFW alone is not proof that the admin panel is private.

Run on your own computer in Terminal or PowerShell, replacing both placeholders.
ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:51821:127.0.0.1:51821 USER@PUBLIC_IPV4

Keep that terminal open and visit http://127.0.0.1:51821 on the same computer. Verify the SSH host fingerprint on first connection. Create a strong administrator password, choose a new installation, and enter the VPS public IPv4 as Host with port 51820. The VPN endpoint must be the public address, never 127.0.0.1, a Docker address, or a URL beginning with http://.

4. Connect Windows, macOS, iPhone, and Android

Create a separate client profile for each device, named clearly—for example, Work laptop or Personal phone. Reusing one profile across simultaneous devices can cause connection problems and makes revocation harder.

DeviceImport method
Windows or macOSInstall the official WireGuard app, download the client .conf file, and import it as a tunnel.
iPhone, iPad, or AndroidInstall the official WireGuard app, add a tunnel, and scan the client QR code shown on your computer.

Treat configuration files and QR codes as credentials: they contain private keys. Transfer them privately and revoke the profile if exposed. For full IPv4 routing, confirm the client AllowedIPs includes 0.0.0.0/0; separately address IPv6 as explained above.

5. Verify the connection and fix common problems

  1. Activate the client and confirm a recent handshake and increasing transfer counters in wg-easy.
  2. Compare your public IPv4 before and after connection; with full IPv4 routing, the connected result should match your VPS egress address.
  3. Check DNS resolution and IPv6 separately. Repeat after switching between Wi-Fi and cellular, then test what happens when the VPN disconnects.
  4. From outside the VPS, verify that its public address on TCP 51821 does not open the admin page.
SymptomCheck first
No handshakePublic endpoint, UDP firewall rule, client keys, and whether the local network blocks UDP.
Handshake but no browsingClient routes, configured DNS, forwarding, and Docker/host firewall conflicts.
Admin page unavailableSSH tunnel still running, container status, and whether another app uses local port 51821.
Container restarts repeatedlyContainer logs, kernel WireGuard support, and capability restrictions on the VPS.
cd /opt/wg-easy
sudo docker compose logs --tail=80 vpn
sudo docker compose exec vpn wg show

Keep your self-hosted VPN maintained

Before updates, review release notes and schedule a short interruption. Run docker compose down, back up compose.yaml and the data directory to protected storage, then start the service again. The backup contains VPN credentials. Never publish it or use docker compose down --volumes as an update shortcut.

After making the backup, update within the v15 image track.
cd /opt/wg-easy
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps

Retest a client, remove unused profiles, and keep the host and Docker patched. A personal VPN changes where traffic exits; it does not eliminate trust in the VPS operator or make browsing anonymous.

Common questions

Do I need a domain name for wg-easy?

No. A reachable public IPv4 address works as the WireGuard endpoint. A domain becomes useful if you later deploy an HTTPS administration interface.

Can I close the SSH tunnel after setup?

Yes. The tunnel is only for the admin interface. WireGuard clients continue connecting directly to the VPS over UDP 51820.

Does wg-easy include a VPN kill switch?

Do not assume one is enabled. Routing and fail-closed behavior depend on the client operating system and profile. Configure and test client controls, including IPv6 handling, separately.

Official documentation

Documentation reviewed October 18, 2026. Check the linked project documentation before installing or upgrading; commands below have not been run on your VPS.