Co-Authored-By: Claude Opus 5.5 <[email protected]>
Wireguard UI
An admin dashboard for Wireguard — manage hosts, clients, tunnels, and IP rules through a web interface.
Features
- Host Management — Create and configure WireGuard hosts with public keys, IP addresses, and listen ports
- Client Management — Add clients per host with WireGuard configs, allowed IPs, and notes
- Client Rules — Define IP table rules per client (rule type, destination, ports, protocol)
- Media Management — Upload and manage media assets (images, documents, etc.) with tagging, thumbnails, and RAG ingestion
- User Management — Multi-user support with role-based access (user types)
- Persistent Storage — SQLite database with automatic schema generation
- WebSocket Support — Real-time updates via WebSocket connections
- Responsive UI — Tailwind CSS v4 with twui design system components
Prerequisites
- Bun 1.3.1 or later (install)
- SQLite (via
@moduletrace/bun-sqlite) - git
- zip and unzip
- Linux (Debian, Ubuntu, Fedora, Arch, Alpine, or similar)
- Root access (for installation script and system service setup)
Installation
Automatic (Recommended)
Run the install script on a fresh machine:
curl -fsSL https://url.tben.me/wg-ui | sudo bash
Or clone the repo and run the script manually:
git clone https://git.tben.me/Moduletrace/wireguard-ui.git
cd wireguard-ui
bash src/scripts/install-wg-ui.sh
The install script will:
- Install system dependencies (git, curl, ca-certificates, openssl)
- Install Bun 1.3.14 to
/opt/bun - Set up library directories under
/var/lib/wgui(keys, clients, hosts, iptables, scripts) - Clone or update the repo to
/var/lib/wgui/webapp - Install app dependencies (
bun install) - Generate
.envwith encryption secrets - Run WireGuard setup (if available)
- Install Docker (if missing) and run Caddy as the
wgui-caddycontainer - Install the
wg-quickhelper script - Set up systemd/OpenRC services for
wguiand certificate renewal
The installer never modifies your firewall — it will print the commands to temporarily allow the dashboard port so you can reach the first login, and then point you to Setup → Firewall for reviewing and removing rules.
Firewall
Firewall rules are managed per host, automatically:
- Bootstrap (manual, temporary) — allow the dashboard port so you can
reach the web UI once:
ufw allow 10752/tcp # ufw (Debian/Ubuntu) firewall-cmd --permanent --add-port=10752/tcp && firewall-cmd --reload # firewalld (RHEL/Fedora) - Host creation — every host setup automatically allows its entire
wireguard subnet (e.g.
10.5.0.1→10.5.0.0/24from anywhere) and the host's wireguard listen port (e.g.51821/udp) in ufw or firewalld. Detailed per-client traffic rules are enforced by the existing iptables chain scripts, not the host firewall. - Host deletion — the host's subnet and listen-port rules are removed automatically.
- Cleanup — once everything is reachable via the tunnel, remove the
temporary bootstrap rule and review all rules under Setup → Firewall:
Rules that no longer match any host (
ufw delete allow 10752/tcp firewall-cmd --permanent --remove-port=10752/tcp && firewall-cmd --reloadtemporary/orphaned) are flagged with copyable removal commands on the setup page.
TLS for Tunnel Services
Client routes expose internal services to WireGuard clients over HTTPS. The
data plane is a dedicated Caddy container (wgui-caddy, host networking) on
8443; wg-ui owns the route and certificate records and regenerates the
Caddyfile. Caddy never binds ports 80 or 443, and the host's existing proxy is
left alone. Docker is required — the installer starts the daemon if it is not
running.
-
If Caddy isn't installed yet, open Setup → TLS ingress and click Install Caddy (or use the Caddy action on Setup → Maintenance). The installer pulls
caddy:2.11.4-alpine, (re)creates thewgui-caddycontainer, and sets up certificate support. -
Open a route form from Routes (or a host's Routes panel) and set the ACME email and staging toggle. No DNS provider token is needed: certificates use manual DNS-01.
Let's Encrypt requires a public domain you control; internal-only names such as
home.arpaor.localneed a private CA instead. -
Add an A/AAAA record for each route hostname pointing at the host's WireGuard IP. Caddy also restricts each route to the subnet of the host that owns it.
-
Open the route page and click Issue (or Renew). The dashboard shows the exact
_acme-challenge.<hostname>TXT record. Add it at Cloudflare, wait for propagation, then click Validate. -
Clients use
https://<hostname>:8443/. Portless URLs require the host's existing 443 proxy to forward to127.0.0.1:8443; wg-ui does not take over 443. Caddy allows localhost so that proxy can reach it, which means the proxy itself must enforce who may use those portless URLs.
Manual DNS-01 certificates are renewed from the route page because validation
needs your registrar. The route list shows expiry warnings. A successful
issuance reloads Caddy without restarting wg-ui. Check
docker logs wgui-caddy for reload failures.
Upgrading from the old native Caddy install
Earlier versions ran Caddy as a host binary in /opt/caddy with a wgui-caddy
systemd/OpenRC service. Re-running the installer migrates the host in place: it
pulls the image, starts the container, and only then removes the old service
and binary. The Caddyfile is rewritten to use the new admin socket path
(/var/lib/wgui/caddy/admin.sock); certificates in /var/lib/wgui/tls are
untouched.
To remove a leftover native install without installing the container:
sudo bash src/scripts/install-caddy.sh --purge-legacy
Add --purge to also remove the container, the image and the certificate
renewal job (the Caddyfile and certificates are always kept):
sudo bash src/scripts/install-caddy.sh --purge-legacy --purge
Manual (Development)
git clone https://git.tben.me/Moduletrace/wireguard-ui.git
cd wireguard-ui
bun install
NODE_ENV=development bun run dev
The dev server runs on port 10752 by default.
Configuration
Environment Variables
The .env file (gitignored) controls runtime behavior. It is auto-generated during installation if missing:
| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Environment (development/production) |
production |
ENCRYPTION_KEY |
Encryption key for sensitive data | Auto-generated |
ENCRYPTION_SALT |
Encryption salt for sensitive data | Auto-generated |
DATA_DIR |
Application data directory | $INSTALL_DIR/.data |
CADDY_VERSION |
Caddy image tag (caddy:<version>-alpine) |
2.11.4 |
CADDY_HTTPS_PORT |
Caddy ingress HTTPS port (never 80/443) | 8443 |
CADDY_HTTP_PORT |
Caddy plain HTTP port (never 80) | 8081 |
CADDY_INGRESS_ENABLED |
Set to 0 to skip Caddy config sync |
1 |
ACME_STAGING |
Set to 1 to use the Let's Encrypt staging CA |
0 |
Database Configuration
Edit bun-sqlite.config.ts to change database settings:
const config: BunSQLiteConfig = {
db_name: "wgui", // Database file name
db_dir: "./db", // Database directory
typedef_file_path: "./db/types/db.ts", // Generated types
db_schema_file_name: "schema.ts", // Schema definition
};
Server Port
The server port is defined in src/data/site-data.ts:
export const SiteData = {
ServerPort: 10752,
// ...
} as const;
Available Scripts
| Command | Description |
|---|---|
bun run dev |
Start development server (requires root) |
bun run start |
Start production server |
bun run db:schema |
Generate/update database schema and types |
bun run db:admin |
Open database admin interface |
Project Structure
wireguard-ui/
├── src/
│ ├── pages/ # File-system based routes (__root.tsx, index.tsx, etc.)
│ │ ├── (sections)/ # Page sections
│ │ ├── (partials)/ # Page-local components
│ │ ├── (data)/ # Page-local data
│ │ ├── (functions)/ # Page-local functions
│ │ ├── (utils)/ # Page-local utilities
│ │ └── (hooks)/ # Page-local hooks
│ ├── components/
│ │ ├── twui/ # Local design system (publishable package)
│ │ ├── ui/ # shadcn/ui components
│ │ ├── shadcnblocks/ # shadcnblocks registry components
│ │ └── general/ # Shared page components (chat, date-picker, logo, etc.)
│ ├── hooks/ # Shared React hooks
│ ├── functions/
│ │ ├── backend/ # Server-only: DB queries, email, media upload, RAG
│ │ ├── frontend/ # Client-safe: CRUD handlers, fetch handlers
│ │ └── general/ # Platform-agnostic: join builders
│ ├── utils/ # Utility modules (auth, crypto, CSRF, hashing, etc.)
│ ├── dict/ # Constants and dictionaries (user types, gates, etc.)
│ ├── data/ # Static data (app constants, location data, etc.)
│ ├── types/ # Type definitions
│ ├── scripts/ # Install and setup scripts
│ └── server.ts # Entry point — starts Bun.serve with WebSocket support
├── db/
│ ├── schema.ts # Database schema definition
│ └── types/db.ts # Generated database types
├── public/ # Static assets served by the server
├── assets/ # Project assets
├── bun-sqlite.config.ts # Database configuration
├── bunext.config.ts # Bunext configuration
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── .env # Environment variables (gitignored)
Database Schema
The database consists of the following tables:
| Table | Description |
|---|---|
users |
User accounts (name, email, username, password) |
user_types |
User type assignments per user |
media |
Media assets (images, documents, etc.) |
media_paradigms |
Media paradigm classifications per media item |
clients |
WireGuard clients per host |
hosts |
WireGuard host configurations |
client_rules |
IP table rules for each client |
client_routes |
HTTPS routes exposed to WireGuard clients |
tls_certificates |
Certificates used by the Caddy ingress |
variables |
Application-wide key-value variables |
Generate types from the schema:
bun run db:schema
Admin Route Hierarchy
| Route Pattern | Access | Features |
|---|---|---|
/admin |
Admin | Dashboard overview |
/admin/hosts |
Admin | Host management (add, edit, per-host detail) |
/admin/routes |
Admin | Global HTTPS route list with host and certificate status |
/admin/hosts/[host_id]/routes |
Admin | HTTPS routes and upstreams exposed to tunnel clients |
/admin/hosts/[host_id]/routes/[route_id] |
Admin | Route details, TLS settings and certificate issue/renew |
/admin/clients |
Admin | Client management (add, edit, client rules per host) |
/admin/settings |
Admin | Account settings |
/admin/setup |
Super admin | System setup (hosts, WireGuard tools, update, restart) |
/admin/setup/tls |
Super admin | Caddy ingress installation and status |
Development
Development Mode
Set NODE_ENV=development and run:
NODE_ENV=development bun run dev
In development mode, the install script skips cloning and system service setup — the server is assumed to already be running.
Path Alias
@/* resolves from the repository root. Examples:
import { Button } from "@/src/components/ui/button";
import type { BUN_SQLITE_WGUI_USERS } from "@/db/types/db";
Type-Checking
Run type checks with:
bunx tsc --noEmit
Testing
Run individual test files:
bun test path/to/test.file.test.ts
Repository
- URL: https://git.tben.me/Moduletrace/wireguard-ui
- Install Script: src/scripts/install-wg-ui.sh