Skip to content

Self-hosted WireGuard access control

One console. Every WireGuard exit.

Issue peers, watch handshakes and manage exits across regions. Self-hosted, MFA-gated and reachable without inbound management ports.

Topology preview
4 exits

Illustrative topology. Peer traffic stays on the exit you assign.

node poll interval
15snode poll interval
inbound management ports
0inbound management ports
to add a node
~2 minto add a node
console for every exit
1console for every exit

The mesh is a console and its agents.

Every node dials out to the console. Every peer attaches to the exit you assign. Nothing in between needs a public management port.

Control plane runs over HTTPS. Peer traffic stays between the device and its exit.

See it in the demo

A new exit joins with one command.

Create the node, copy its token and run the installer on the host. The agent dials out, picks up its WireGuard config and keeps it applied.

  • Outbound HTTPS only
  • 15s poll
  • Reapplies after reboot
On the exit host
curl -fsSL https://raw.githubusercontent.com/garyhooi/wireguard-console/main/node-install.sh \
  | sudo bash -s -- <TOKEN> https://console.example.com <NODE_ID>

Everything you issue, watch and revoke.

The admin surface is the product. Peers, users, exits, filtering and audit live in one place, with no separate config repo to drift.

console.example.com / dashboardsample data
WireGuard Console dashboard showing peers, connected devices, users and a recent peer table
The dashboard from a live install. Every peer row is a device with its own key and tunnel address.
  • Peers and people

    Issue, suspend and revoke devices. Public keys and tunnel IPs are generated for you.

  • Handshakes and status

    See which peers are up and when they last handshook. Traffic charts cover the console host.

  • DNS filtering

    Block domain groups globally, per user or per department through AdGuard, with a branded block page.

  • IP blocking

    Drop IPv4 and IPv6 addresses or ranges before NAT, globally or per user, with reusable groups.

  • Activity and audit

    Review web activity on the console exit, export usage as CSV and follow every admin action.

Admin access is not an afterthought.

The console issues the keys, so the console is the thing worth protecting. Every session carries a second factor, and every admin can enroll before they touch a peer.

  • Mandatory second factor

    Every admin enrolls a passkey or an authenticator app before the first session is issued.

  • Passwordless sign-in

    A saved passkey can replace the password entirely, with Face ID, Touch ID or a security key.

  • Step-up for destructive work

    Restores and other irreversible actions ask for MFA again, even inside a signed-in session.

  • Recovery that stays usable

    Single-use recovery codes, plus a super admin reset path when a factor is lost.

Read the MFA and passkey notes
console.example.com / enrollrequired
MFA enrollment screen with a passkey option and an authenticator app option
Enrollment happens before the first session. Passkeys and authenticator apps can both be enrolled.

Built for the boring parts.

Filtering, blocking, reconnects and backups. The work between a handshake and a clean audit trail.

Filter where the tunnel ends

AdGuard rules run on the console exit. Reusable domain groups, department scope and a branded block page for denied requests.

domain-group / ads-and-trackersblock
domain-group / socialblock
domain-group / streamingblock
domain-group / malwareblock

Drop ranges before NAT

IPv4 and IPv6 rules are enforced server-side before forwarding. Block an address for everyone or for one peer.

198.51.100.0/24drop
203.0.113.42drop
2001:db8::/32drop

Documentation ranges shown

Agents that reapply

After a reboot, the node agent reapplies its interfaces on the next poll.

rebootpoll 15swg apply

Backups gated by MFA

Create, download and restore from the console. Destructive restores ask for a fresh factor.

backup.tar.zst
restore: mfa_required

Apache 2.0, no seats

Self-host the whole stack. No hosted control plane and no per-admin licence.

Read the license

A new node in about two minutes.

Run the installer on Ubuntu 22.04+ or Debian 12+. Add a node from the console, paste one join command and give it a subnet nobody else uses.

  1. Install the console

    Ubuntu 22.04+ or Debian 12+. The installer adds Docker, starts the stack and prints your first admin password.

  2. Join the node

    Create a node in the console, run its join command and open the WireGuard UDP port on that host.

  3. Create a server

    Give the exit a public endpoint, a subnet that no other node uses, and DNS servers for peers.

  4. Issue a peer

    Assign a person or device to that exit, deliver the config and watch the first handshake.

curl -fsSL https://raw.githubusercontent.com/garyhooi/wireguard-console/main/install.sh | sudo bash

Installs Docker, starts the stack and prints the first admin password.

Host OS
Ubuntu 22.04+ / Debian 12+
Console host
Docker, Postgres, Caddy, AdGuard
Exit host
Docker, node agent, WireGuard
Ports
80/tcp, 443/tcp, 51820/udp
Read the setup guideUbuntu 22.04+ / Debian 12+ / Docker / domain or public IP

Questions, answered.

What runs where, what a node reports and what you need before the first install.

Read the full README
Is WireGuard Console a hosted VPN service?

No. It is a self-hosted web console and a node agent for the WireGuard servers you run. The stack is Docker, PostgreSQL and Caddy, and the code is Apache 2.0.

Which operating systems are supported?

Ubuntu 22.04+ and Debian 12+ are supported on the console host and on exit nodes. The installer handles Docker, WireGuard and systemd on either distribution.

Do remote nodes need inbound management ports?

No. The node installer needs one SSH session. After that the agent dials out to the console over HTTPS every 15 seconds, so only the WireGuard UDP port has to be public.

Do I need a domain to install it?

You can install on a public IP and let Caddy use its internal CA, with a one-time browser warning. Passkeys need a hostname, so use a domain if you want passwordless sign-in.

How long does a new node take?

Create the node in the console, run the join command on an Ubuntu 22.04+ or Debian 12+ host, then wait about 30 seconds for its first poll. The README estimates roughly two minutes including the install.

Do remote nodes report traffic and browsing?

Not today. Traffic charts and web activity cover the console host's AdGuard instance. Node agents apply WireGuard interfaces and keep them applied after a reboot.

What runs on each machine?

The main host runs the console stack, Caddy, PostgreSQL and AdGuard Home. Each exit runs Docker, the node agent and WireGuard. DNS servers are set per VPN server, so peers never default to an empty resolver.

How do backup and restore work?

System, then Backups, creates and downloads a backup. A restore requires MFA confirmation. Backups live on the console's Docker volume, so download a copy off the server.

What is the licence?

Apache 2.0. You can run it internally, modify it or fork it without a per-admin fee.

Run it where your keys live.

Self-host the console, put an exit in the region your team works in and keep the admin surface behind a second factor.