Deploy wg-easy v15 for WireGuard

The wg-easy project provides a web interface for managing WireGuard VPN servers. Version 15 introduces a first-boot setup wizard while maintaining kernel-level packet routing controls. This guide covers deployment using Docker Compose with explicit IPv4-only configuration to avoid boot loops and routing errors on servers without IPv6 support.

Prerequisites

The following items are required:

  • Linux server with Docker Engine and Docker Compose installed
  • Kernel module support for WireGuard
  • Port 51820/udp open in firewall
  • Root or sudo access to create directories and manage containers

Configuration Parameters

The wg-easy v15 container requires specific environment variables, Linux capabilities, and kernel parameters to function correctly in an IPv4 environment.

Environment Variables

The INSECURE variable controls HTTPS cookie security for the web interface. Set to true when accessing the UI directly over HTTP during initial setup. Set to false when the container sits behind an SSL reverse proxy like Nginx Proxy Manager.

Linux Capabilities

Two capabilities must be granted to the container:

  • NET_ADMIN allows the container to manage network interfaces, configure iptables rules, and modify host routing tables
  • SYS_MODULE allows the container to load and manage WireGuard kernel modules

Kernel Parameters

Two sysctl settings are required for proper packet routing:

  • net.ipv4.ip_forward=1 enables IPv4 packet forwarding so traffic can route between WireGuard clients and external networks
  • net.ipv4.conf.all.src_valid_mark=1 is required by WireGuard policy routing to track interface packet marks

Create Environment File

Create a deployment directory and environment file to isolate configuration from the Compose definition:

mkdir -p /opt/containers/wg-easy
cd /opt/containers/wg-easy

Create a .env file with the following content:

# Storage Configuration
ROOT=/opt/containers

# Web UI Access
UI_PORT=51821

# Internal Network Addressing (IPv4 Only)
STATIC_IPV4=10.0.0.42

Create Docker Compose Configuration

Create a docker-compose.yml file in the same directory. This configuration explicitly removes all IPv6 sysctls, subnets, and address assignments:

version: "3.8"

networks:
  cloudflare:
    external: true
  wg:
    driver: bridge
    ipam:
      config:
        - subnet: 10.0.0.0/24

services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:15
    container_name: wg-easy
    environment:
      - INSECURE=true
    networks:
      cloudflare: {}
      wg:
        ipv4_address: ${STATIC_IPV4}
    volumes:
      - ${ROOT}/wg-easy/config:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
      - "${UI_PORT}:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:51821"]
      interval: 30s
      timeout: 15s
      retries: 3
      start_period: 30s

This configuration assumes an external network named cloudflare already exists. If you are not using Nginx Proxy Manager or another reverse proxy on this network, remove the cloudflare network references.

Deploy Container Stack

Create the persistent configuration directory:

mkdir -p /opt/containers/wg-easy/config

Start the container:

docker compose up -d

Verify the container is running:

docker ps

Check container logs for errors:

docker logs wg-easy

Complete Initial Setup

Open a web browser and navigate to http://SERVER_IP:51821. The wg-easy v15 setup wizard will appear.

Configure the following settings:

  • Administrative password for web UI access
  • Server public IPv4 address or fully qualified domain name
  • DNS server for VPN clients (examples: 1.1.1.1, 8.8.8.8, 9.9.9.9)

After completing the wizard, the web interface displays options to create client configurations. Generate a new client profile and download the configuration file or scan the QR code with the WireGuard mobile application.

Firewall Configuration

Ensure port 51820/udp is accessible from client networks. If using UFW, add the following rule:

ufw allow 51820/udp

Port 51821/tcp provides access to the web interface. Restrict this port to trusted networks or place wg-easy behind a reverse proxy with authentication.

Troubleshooting Common Issues

Container Fails to Start with IPv6 Errors

If the container enters a restart loop with IPv6-related errors in the logs, verify that all IPv6 sysctl parameters are removed from the docker-compose.yml file. Some base images attempt to enable IPv6 forwarding even when the host kernel has IPv6 disabled.

Clients Connect but Cannot Route Traffic

Verify that net.ipv4.ip_forward is enabled on the host system:

sysctl net.ipv4.ip_forward

The output should show net.ipv4.ip_forward = 1. If the value is 0, the sysctl setting in the Compose file is not being applied correctly. Check Docker version compatibility with the sysctls directive.

Web Interface Returns Connection Refused

Check that the UI_PORT variable in the .env file matches the external port mapping in the docker-compose.yml ports section. Verify the container is listening on port 51821 internally:

docker exec wg-easy netstat -tuln | grep 51821

Placing wg-easy Behind a Reverse Proxy

When deploying wg-easy behind Nginx Proxy Manager or another reverse proxy, change the INSECURE environment variable to false. Configure the reverse proxy to forward requests to the wg-easy container name on port 51821.

The WireGuard VPN port 51820/udp must remain directly exposed on the host. UDP traffic cannot be proxied through standard HTTP reverse proxies.

AI assistance is used on this site for language, formatting, and turning research into a consistent template. It is not used to perform the underlying research or verify technical claims. Every command, configuration, and step in this post is tested by hand before publication.

Leave a Reply

Your email address will not be published. Required fields are marked *

2 × two =