WireGuard Management
Create and manage VPN peers with automatic IPv4/IPv6 allocation, QR code provisioning, and downloadable .conf files.
A clean web interface for managing WireGuard peers, firewall rules, and user authentication — without depending on any third-party cloud service. Fully transparent, fully yours.
Built for teams and individuals who want full control over their VPN infrastructure.
Create and manage VPN peers with automatic IPv4/IPv6 allocation, QR code provisioning, and downloadable .conf files.
Per-user nftables chains with CIDR, protocol, and port range support. Network policies apply instantly.
TOTP authenticator apps (Google Authenticator, Authy, 1Password) and WebAuthn security keys (YubiKey, Touch ID).
OpenID Connect and SAML 2.0 with auto-provisioning. Works with Okta, Auth0, Keycloak, Azure AD, and more.
Full-featured /api/v0 with token authentication for programmatic management of users, devices, rules, and configuration.
Live RX/TX counters, traffic charts, handshake tracking, WAN connectivity checks, and system notifications.
Passwordless email-based login when SMTP is configured. No password to remember, no friction for users.
Configurable VPN session durations with automatic peer expiry. Users re-authenticate on schedule.
User-selectable dark/light theme stored in profile. Auto mode follows system preference.
A clean, intuitive interface for every aspect of VPN management.
Docker Compose is the recommended way to deploy WireGUI.
# Clone the repository
git clone https://github.com/bartei/wiregui.git
cd wiregui
# Start everything
docker compose -f compose.prod.yml up -d
The container runs migrations automatically, manages the WireGuard interface, and requires
NET_ADMIN +
SYS_MODULE capabilities.
# Install dependencies
uv sync
# Start PostgreSQL & Valkey
docker compose up -d
# Run migrations and start
alembic upgrade head
uv run python -m wiregui.main
Open http://localhost:13000
— an admin account is created on first run (check the logs for the generated password).
Web UI & API
:13000 TCP
WireGuard VPN
:51820 UDP
Python
3.13+ required
Everything you need to deploy and configure WireGUI. See the full wiki for detailed guides.
Multiple authentication methods can be used individually or in combination.
Default email/password login. Can be disabled when SSO is configured.
Integrate with Okta, Auth0, Keycloak, Azure AD, Google Workspace, or any OIDC-compliant provider.
Enterprise single sign-on with auto-provisioning and metadata configuration.
Passwordless email-based login. Requires SMTP configuration.
Time-based one-time passwords. Compatible with Google Authenticator, Authy, 1Password.
Hardware security keys (YubiKey) and platform authenticators (Touch ID, Windows Hello).
WireGUI is distributed as a Docker image. Migrations run automatically on startup.
server {
listen 443 ssl;
server_name vpn.example.com;
location / {
proxy_pass http://localhost:13000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
} vpn.example.com {
reverse_proxy localhost:13000
} Caddy handles TLS certificates and WebSocket proxying automatically.
The database is the single source of truth. WireGuard interface state is fully reconciled from the database on startup.
docker compose exec postgres pg_dump -U wiregui wiregui > backup.sql
When "Allow local network to reach peers" is enabled in Admin → Rules → Network Policies, WireGUI configures its firewall to accept traffic from your LAN to VPN peers. However, your LAN's default gateway (router) also needs a static route so it knows to send traffic destined for the WireGuard subnet to the WireGUI server instead of out to the internet.
Without this route, LAN devices can't reach VPN peers even if the policy is enabled — the router will forward WireGuard-subnet traffic to its WAN gateway where it gets dropped. The static route tells the router: "to reach the VPN subnet, forward packets to the WireGUI server."
In the examples below, we use the default WireGUI subnets and assume the WireGUI server's LAN IP is 192.168.1.100.
Adjust these values to match your environment.
| Value | Example | Source |
|---|---|---|
| WireGuard IPv4 subnet | 10.3.2.0/24 | WG_WG_IPV4_NETWORK |
| WireGuard IPv6 subnet | fd00::3:2:0/120 | WG_WG_IPV6_NETWORK |
| WireGUI server LAN IP | 192.168.1.100 | Your network |
Run on the gateway, or add to /etc/network/interfaces / netplan for persistence.
# IPv4
ip route add 10.3.2.0/24 via 192.168.1.100
# IPv6
ip -6 route add fd00::3:2:0/120 via <server-ipv6-lan-addr> To persist with netplan:
# /etc/netplan/01-static-routes.yaml
network:
version: 2
ethernets:
eth0:
routes:
- to: 10.3.2.0/24
via: 192.168.1.100 Most consumer and business routers have a static routes section under Advanced or Routing.
| Field | Value |
|---|---|
| Destination | 10.3.2.0 |
| Subnet Mask | 255.255.255.0 |
| Gateway / Next Hop | 192.168.1.100 |
| Interface | LAN |
/ip route add dst-address=10.3.2.0/24 \
gateway=192.168.1.100
/ipv6 route add dst-address=fd00::3:2:0/120 \
gateway=<server-ipv6-lan-addr> set protocols static route 10.3.2.0/24 \
next-hop 192.168.1.100
set protocols static route6 fd00::3:2:0/120 \
next-hop <server-ipv6-lan-addr>
commit; save Navigate to System → Routes → Configuration and add a new gateway and route.
| Field | Value |
|---|---|
| Destination network | 10.3.2.0/24 |
| Gateway | 192.168.1.100 |
ip route 10.3.2.0 255.255.255.0 192.168.1.100
ipv6 route fd00::3:2:0/120 <server-ipv6-lan-addr> From any LAN device, confirm the route is working:
# Should show the WireGUI server (192.168.1.100) as the next hop
traceroute 10.3.2.1
# Ping a connected peer (the peer must be online)
ping 10.3.2.2 If the traceroute shows your WAN gateway instead of the WireGUI server, the static route is missing or not applied.
All settings use the WG_ prefix and can be set via environment variables or a .env file.
| Variable | Default | Description |
|---|---|---|
WG_DATABASE_URL | postgresql+asyncpg://wiregui:wiregui@localhost/wiregui | PostgreSQL connection string |
WG_REDIS_URL | redis://localhost:6379/0 | Valkey/Redis connection |
WG_SECRET_KEY | change-me-in-production | JWT signing + Fernet encryption key |
WG_WG_ENABLED | false | Enable WireGuard interface management |
WG_WG_ENDPOINT_HOST | localhost | Public endpoint for client configs |
WG_WG_ENDPOINT_PORT | 51820 | WireGuard listen port |
WG_WG_IPV4_NETWORK | 10.3.2.0/24 | IPv4 tunnel network |
WG_WG_IPV6_NETWORK | fd00::3:2:0/120 | IPv6 tunnel network |
WG_ADMIN_EMAIL | admin@localhost | Initial admin email |
WG_ADMIN_PASSWORD | (auto-generated) | Initial admin password |
WG_EXTERNAL_URL | http://localhost:13000 | Public-facing URL |
WG_IDP_CONFIG_FILE | (none) | Path to YAML with OIDC/SAML IdP definitions |
Full programmatic access via /api/v0.
Manage users, devices, rules, and configuration with API tokens.
curl -X POST https://vpn.example.com/api/v0/devices/ \
-H "Authorization: Bearer wgui_xxxx" \
-H "Content-Type: application/json" \
-d '{"name": "laptop", "user_id": 1}'
Generate API tokens from the
Account Settings
page. Tokens use the Authorization: Bearer header.
All endpoints require API token authentication
/api/health no auth /api/v0/users/ List all users
/api/v0/users/ Create user
/api/v0/devices/ List all devices
/api/v0/devices/ Create device
/api/v0/rules/ List firewall rules
/api/v0/rules/ Create rule
/api/v0/configuration/ Get configuration
/api/v0/configuration/ Update configuration
This project exists because infrastructure software should serve its users, not its investors. Too many VPN tools have been enshittified — features locked behind paid tiers, telemetry quietly added, self-hosting made deliberately painful.
WireGUI is AGPL-licensed specifically to prevent this. If you run it, you own it. If you modify it and offer it as a service, you share the source. No bait-and-switch, no open-core grift, no "community edition" that mysteriously lacks the features you actually need.
Software that manages your network traffic should be fully transparent and fully yours.