VPNSmith
Free tool - wg-easy v15

wg-easy docker-compose generator

Fill in your server and how you want to reach the admin panel. You get a docker-compose.yml that matches the current wg-easy v15 image, a Caddyfile if you want HTTPS, and the exact commands to start it. Everything is built in your browser.

Everything is generated in your browser. Nothing you type, including the password, is sent to our servers.

The address WireGuard clients connect to.

How will you reach the admin panel?

An A record for this name must point to the server, with ports 80 and 443 open.

Client traffic
  • Click « New » to generate the admin password.
  • Enter the panel domain: Caddy needs it to obtain the certificate.
docker-compose.yml
# wg-easy v15 - generated on vpnsmith.com/en/tools/wg-easy-compose-generator
# Host: vpn.example.com - WireGuard port: 51820/udp - panel: https://wg.example.com

volumes:
  etc_wireguard:
  caddy_data:
  caddy_config:

services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:15
    container_name: wg-easy
    environment:
      - PORT=80
      - TRUSTED_PROXIES=10.42.42.43,fdcc:ad94:bacf:61a3::2b
      - INIT_ENABLED=true
      - INIT_USERNAME=admin
      - INIT_PASSWORD=GENERATE-A-PASSWORD
      - INIT_HOST=vpn.example.com
      - INIT_PORT=51820
      - INIT_DNS=1.1.1.1,9.9.9.9
    networks:
      wg:
        ipv4_address: 10.42.42.42
        ipv6_address: fdcc:ad94:bacf:61a3::2a
    volumes:
      - etc_wireguard:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"   # WireGuard: the only port clients need
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv6.conf.all.forwarding=1
      - net.ipv6.conf.default.forwarding=1

  caddy:
    image: caddy:2-alpine
    container_name: caddy
    ports:
      - "80:80/tcp"
      - "443:443/tcp"
      - "443:443/udp"
    networks:
      wg:
        ipv4_address: 10.42.42.43
        ipv6_address: fdcc:ad94:bacf:61a3::2b
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped

networks:
  wg:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 10.42.42.0/24
        - subnet: fdcc:ad94:bacf:61a3::/64
Caddyfile
wg.example.com {
	reverse_proxy wg-easy:80
}
install.sh
# 1. Firewall: SSH first, then the WireGuard port
sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw enable
# Note: ports published by Docker bypass ufw. Also open/close them in your provider firewall.

# 2. Create the folder and paste the generated file(s)
sudo mkdir -p /etc/docker/containers/wg-easy
cd /etc/docker/containers/wg-easy
sudo nano docker-compose.yml
sudo nano Caddyfile

# 3. Start wg-easy
sudo docker compose up -d
# Watch the logs (Ctrl+C to quit)
sudo docker compose logs -f wg-easy

# 4. Open the panel: https://wg.example.com

# 5. Once logged in, remove the INIT_* lines and run docker compose up -d again

This file needs a server with a public IP

A Contabo Cloud VPS gives you a dedicated IPv4, Docker-ready Linux images and unlimited traffic, which is all wg-easy needs.

Affiliate link: we earn a commission at no extra cost to you.

See the Contabo VPS offer

Why your old wg-easy compose file stopped working

wg-easy v15 is a full rewrite. Most environment variables from v14 tutorials are simply ignored by the new image: the container starts, but your host, password and DNS are not applied. The official migration guide states that most settings moved to the admin panel. This is how the old variables map to v15:

v14 variablev15 equivalent
WG_HOSTINIT_HOST (first start only), then the admin panel
PASSWORD / PASSWORD_HASHINIT_USERNAME + INIT_PASSWORD, or the setup wizard
WG_PORTINIT_PORT, plus the matching UDP port mapping
WG_DEFAULT_DNSINIT_DNS
WG_DEFAULT_ADDRESSINIT_IPV4_CIDR together with INIT_IPV6_CIDR
Panel over plain HTTPINSECURE=true is now required

Three ways to reach the admin panel

The panel can create VPN clients, so whoever reaches it controls your VPN. Only the WireGuard UDP port has to face the internet.

HTTPS with Caddy is the setup the wg-easy documentation recommends: Caddy obtains a real certificate for a sub-domain that points to your server, and the panel is never served over plain HTTP. The generator puts both containers on the same network, gives Caddy a fixed address and declares it in TRUSTED_PROXIES, so failed logins are logged with the real client IP.

The SSH tunnel publishes the panel on 127.0.0.1 only. Nothing is exposed; you open it with ssh -L when you need it. It is the simplest safe option when you have no domain.

LAN mode is for a home server: the panel is published over HTTP with INSECURE=true. Docker publishes ports through its own iptables rules, so ufw does not filter them; keep that port closed on your router or provider firewall.

Full wg-easy guide: setup, hardening and alternatives →

Frequently asked questions

Does wg-easy v15 still use WG_HOST and PASSWORD_HASH?
No. v15 is a rewrite and these variables are no longer read. The host, port, DNS and admin account are set in the setup wizard on first start, or without any interaction through the INIT_* variables (INIT_ENABLED, INIT_USERNAME, INIT_PASSWORD, INIT_HOST, INIT_PORT). Afterwards they live in the admin panel.
Are the INIT_ variables applied on every restart?
No. They are only used on the very first start, then the setup is disabled. The wg-easy documentation recommends removing them from the compose file once the setup is done, so the password does not stay in clear text on the server.
Why do I have to set INIT_IPV4_CIDR and INIT_IPV6_CIDR together?
The unattended setup works in groups: if you set one variable of a group, you have to set all of them. The two address ranges form one group, which is why the generator always adds the IPv6 range (the wg-easy default) when you change the IPv4 one.
What password does wg-easy accept?
The current code requires at least 2 characters for the username and 12 for the password. The generator creates a 24-character random password in your browser, without the $ character, which Docker Compose would otherwise try to interpolate.
Which server do I need for wg-easy?
A Linux host with Docker, a public IPv4 address and an x86_64 or arm64 CPU. WireGuard is light: a small VPS is enough for a household. Our example host is Contabo, whose entry VPS includes a public IPv4 and unlimited traffic.