β‘ Fast. Simple. Long-lasting.
CΒ³ CELERITY by Click Connect β modern web panel for managing Hysteria 2 and Xray VLESS proxy servers with centralized authentication, one-click node setup, and flexible user-to-server group mapping.
Built for performance: Lightweight architecture designed for speed at any scale.
Dashboard β real-time server monitoring and statistics
Updating an existing installation? See Safe Production Updates.
1. Install Docker (if not installed):
curl -fsSL https://get.docker.com | sh2. Deploy panel (Docker Hub - recommended):
mkdir hysteria-panel && cd hysteria-panel
# Download required files
curl -O https://raw.githubusercontent.com/ClickDevTech/hysteria-panel/main/docker-compose.hub.yml
curl -O https://raw.githubusercontent.com/ClickDevTech/hysteria-panel/main/docker.env.example
# Create Greenlock SSL config (required for HTTPS)
mkdir -p greenlock.d
curl -o greenlock.d/config.json https://raw.githubusercontent.com/ClickDevTech/hysteria-panel/main/greenlock.d/config.json
cp docker.env.example .env
nano .env # Set your domain, email, and secrets
docker compose -f docker-compose.hub.yml up -dAlternative: Build from source (for development or customization)
git clone https://github.com/ClickDevTech/hysteria-panel.git
cd hysteria-panel
cp docker.env.example .env
nano .env # Set your domain, email, and secrets
docker compose up -d3. Open https://your-domain/panel
Planning to manage the panel from AI assistants? See MCP Setup Guide.
Required .env variables:
PANEL_DOMAIN=panel.example.com
ACME_EMAIL=admin@example.com
ENCRYPTION_KEY=your32characterkey # openssl rand -hex 16
SESSION_SECRET=yoursessionsecret # openssl rand -hex 32
MONGO_PASSWORD=yourmongopassword # openssl rand -hex 16- π₯ Web Panel β Full UI for managing nodes and users
- π Dual Protocol β Hysteria 2 and Xray VLESS on one panel
- π Auto Node Setup β Install Hysteria/Xray, certs, port hopping in one click
- π₯ Server Groups β Flexible user-to-node mapping
- βοΈ Load Balancing β Distribute users by server load
- π« Traffic Filtering (ACL) β Block ads, domains, IPs; route through custom proxies
- π Statistics β Online users, traffic, server status
- π± Subscriptions β Auto-format for Clash, Sing-box, Shadowrocket, Hiddify
- π Backup/Restore β Automatic backups with S3 support
- π» SSH Terminal β Direct node access from browser
- π API Keys β Secure external access with scopes, IP allowlist, rate limiting
- πͺ Webhooks β Real-time event notifications with HMAC-SHA256 signing
- πΊ Network Map β Visual cascade topology with Forward/Reverse chain routing (beta)
- π€ MCP Integration β Native AI assistant support (Claude, Cursor, etc.) for panel management
Status: beta β fully functional, but manual verification after deploy is recommended.
Cascade topology allows building server chains where clients connect to one node while traffic exits through another. This is useful in scenarios where the entry point must reside in a specific network or jurisdiction β for example, when connections must originate from local cloud provider IP ranges.
Many corporate and carrier networks apply IP-range filtering. Traffic to well-known local hosting providers may pass unrestricted, while connections to foreign IPs are blocked or throttled. Cascade topology solves this: clients connect to a server in a "trusted" IP range, and traffic is transparently proxied to an external server.
The panel generates Xray-core configurations using the following mechanisms:
| Mechanism | Purpose |
|---|---|
| Reverse Proxy | Xray bridge/portal β allows a server behind NAT to initiate a connection to a public node and receive traffic through it |
| Outbound Chaining | Sequential proxying through multiple outbounds via proxySettings.tag |
| REALITY | TLS handshake camouflage to look like a connection to a legitimate site; no domain or certificate required |
| Transport Layer Proxy | transportLayer: true mode for correct REALITY application in hop chains |
Reverse Proxy β classic Xray reverse scheme. The Bridge server (typically abroad) initiates a persistent connection to the Portal server. Clients connect to Portal, traffic exits through Bridge.
Client βββΆ Portal (entry) βββ tunnel ββ Bridge (exit) βββΆ Internet
(bridge initiates connection)
- Portal can be behind NAT or firewall β no incoming connections required
- Suitable for scenarios where the entry point must be in a specific network
Forward Chain β direct outbound chain. Portal establishes connections through relay nodes to the exit Bridge.
Client βββΆ Portal βββΆ Relay (opt.) βββΆ Bridge (exit) βββΆ Internet
(chained outbounds)
- All nodes in the chain must have a public IP
- REALITY is supported on each hop to encrypt inter-server traffic
Tunnel-REALITY is configured independently from the client-facing REALITY on the Portal node. This enables:
- Encrypting inter-server traffic without drawing attention
- Using different SNI/destination for clients vs. tunnels
- Auto-generating x25519 keys and shortIds when creating links
- Check hop statuses on the Network Map
- Verify traffic exits through the expected Bridge (check exit IP)
- For Forward Chain β confirm each relay is reachable on its
tunnelPort
| Constraint | Reason |
|---|---|
| REALITY + WebSocket | WebSocket doesn't support uTLS fingerprint required by REALITY |
| Forward Chain without public IP | Each hop must accept incoming connections |
| Mixed modes in one chain | Reverse and Forward use different Xray mechanisms and cannot be combined |
| Same port on relay for two hops | A relay that is both bridge and portal requires different ports for incoming and outgoing tunnels |
βββββββββββββββββββ
β CLIENTS β
β Clash, Sing-box β
β Shadowrocket β
ββββββββββ¬βββββββββ
β
hysteria2:// or vless://
β
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββ
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Hysteria Node β β Xray Node β β Hysteria Node β
β :443 + hop β β VLESS Reality β β :443 + hop β
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β β β
β POST /api/auth β CC Agent API β
β GET /online β β
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββ
β HYSTERIA PANEL β
β β
β β’ Web UI (/panel) β
β β’ HTTP Auth API β
β β’ Subscriptions β
β β’ SSH Terminal β
β β’ Stats Collector β
βββββββββββββ¬βββββββββββββ
β
βΌ
ββββββββββββββββββββββββββ
β MongoDB β
ββββββββββββββββββββββββββ
Hysteria:
- Client connects to node with
userId:password - Node sends
POST /api/authto panel - Panel validates user and returns
{ "ok": true/false }
Xray:
- Client connects with UUID (xrayUuid)
- CC Agent on node manages user list via API
- Panel syncs users to node without restarting Xray
Instead of rigid "plans", use flexible groups:
- Create group (e.g., "Europe", "Premium")
- Assign nodes to group
- Assign users to group
- User gets only nodes from their groups in subscription
Fast UDP protocol based on QUIC with port hopping and obfuscation support.
Advantages:
- High speed on unstable networks
- Port hopping to bypass blocks
- Salamander obfuscation
Settings:
- Port, port range for hopping
- ACME or self-signed certificates
- Obfs (Salamander) with password
Modern protocol with Reality support and various transports.
Advantages:
- Reality β disguise as legitimate HTTPS traffic
- Multiple transports (TCP, WebSocket, gRPC, XHTTP)
- No domain required for Reality
Transports:
| Transport | Description | Client Support |
|---|---|---|
| TCP | Direct connection, max speed | All clients |
| WebSocket | Works through CDN and proxies | All clients |
| gRPC | Multiplexing, good for CDN | All clients |
| XHTTP | New splithttp transport | Limited* |
*XHTTP is not supported by all clients (Clash/Sing-box don't support it yet)
Security:
| Mode | Description |
|---|---|
| Reality | Disguise as popular site, no domain needed |
| TLS | Classic TLS with certificate |
| None | No encryption (not recommended) |
-
Add node in panel:
- Type: Xray
- IP, SSH credentials
- Security: Reality (recommended)
- Transport: TCP (recommended for Reality)
-
Click "βοΈ Auto Setup"
-
Panel will automatically:
- Install Xray-core
- Generate Reality keys (x25519)
- Upload config
- Install CC Agent for user management
- Open firewall ports
- Start services
| Field | Description | Example |
|---|---|---|
| Dest | Disguise destination (domain:port) | www.google.com:443 |
| SNI | Server Name Indication | www.google.com |
| Private Key | x25519 private key | Auto-generated |
| Public Key | Public key (for clients) | Auto-generated |
| Short IDs | Session identifiers | Auto-generated |
CC Agent is a lightweight HTTP service on the node for managing Xray users without restart.
Features:
- Add/remove users on the fly
- Traffic stats collection
- Health check
Agent is installed automatically during Xray node auto-setup.
- Main port (443) β Port Hysteria listens on
- Port hopping range (20000-50000) β UDP ports for hopping
- Stats port (9999) β Internal port for stats collection
| Field | Purpose | Example |
|---|---|---|
| Domain | For ACME/Let's Encrypt certificates | de1.example.com β 1.2.3.4 |
| SNI | For masquerading (domain fronting) | www.google.com |
Scenarios:
- Simple setup: Set domain, leave SNI empty
- Domain fronting: Set domain for certs, SNI as popular domain
- No domain: Leave empty β self-signed certificate will be used
- Add node in panel (IP, SSH credentials)
- Click "βοΈ Auto Setup"
- Panel will automatically:
- Install Hysteria 2
- Configure ACME or self-signed certificates
- Set up port hopping
- Open firewall ports
- Start service
Hysteria supports obfuscation to disguise traffic:
- Enable Obfs in node settings
- Set obfuscation password
- Save and update config
Clients will automatically receive obfs params in subscription.
You can run panel and node on the same VPS (panel TCP, node UDP on 443).
Option 1: Use panel domain (recommended)
- Set node domain same as panel domain
- Panel certificates will be copied automatically
Option 2: No domain (self-signed)
- Leave domain field empty
- Self-signed certificate will be generated
All /api/* endpoints (except /api/auth and /api/files) require authentication.
Create a key: Settings β Security β API Keys β Create Key
Usage:
# Option 1 β header
X-API-Key: ck_your_key_here
# Option 2 β Bearer token
Authorization: Bearer ck_your_key_here| Scope | Access |
|---|---|
users:read |
Read users |
users:write |
Create / update / delete users |
nodes:read |
Read nodes |
nodes:write |
Create / update / delete / sync nodes |
stats:read |
Read stats and groups |
sync:write |
Trigger sync, kick users |
Each key has a configurable rate limit (default: 60 req/min).
Exceeded requests return 429 with X-RateLimit-Limit / X-RateLimit-Remaining headers.
Validates user on Hysteria node connection.
// Request
{ "addr": "1.2.3.4:12345", "auth": "userId:password" }
// Response (success)
{ "ok": true, "id": "userId" }
// Response (error)
{ "ok": false }Universal subscription endpoint. Auto-detects format by User-Agent.
| User-Agent | Format |
|---|---|
shadowrocket |
Base64 URI list |
clash, stash, surge |
Clash YAML |
hiddify, sing-box, karing |
Sing-box JSON |
| Browser | HTML page with QR code |
| Other | Plain URI list |
Query params: ?format=clash, ?format=singbox, ?format=uri
Subscription info (status, traffic, expiry).
Required scope: users:read (GET) / users:write (POST, PUT, DELETE)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users |
List users (pagination, filtering, sorting) |
| GET | /api/users/:userId |
Get user |
| POST | /api/users |
Create user |
| PUT | /api/users/:userId |
Update user |
| DELETE | /api/users/:userId |
Delete user |
| POST | /api/users/:userId/enable |
Enable user |
| POST | /api/users/:userId/disable |
Disable user |
| POST | /api/users/:userId/groups |
Add user to groups |
| DELETE | /api/users/:userId/groups/:groupId |
Remove user from group |
| POST | /api/users/sync-from-main |
Sync from external DB |
Required scope: nodes:read (GET) / nodes:write (POST, PUT, DELETE)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/nodes |
List nodes |
| GET | /api/nodes/:id |
Get node |
| POST | /api/nodes |
Create node |
| PUT | /api/nodes/:id |
Update node |
| DELETE | /api/nodes/:id |
Delete node |
| GET | /api/nodes/:id/config |
Get node config (YAML/JSON) |
| GET | /api/nodes/:id/status |
Get node status |
| POST | /api/nodes/:id/reset-status |
Reset status to online |
| GET | /api/nodes/:id/users |
Get users on node |
| POST | /api/nodes/:id/sync |
Sync specific node |
| POST | /api/nodes/:id/update-config |
Push config via SSH |
| POST | /api/nodes/:id/setup |
Auto-setup node via SSH |
| POST | /api/nodes/:id/setup-port-hopping |
Setup port hopping |
| POST | /api/nodes/:id/groups |
Add node to groups |
| DELETE | /api/nodes/:id/groups/:groupId |
Remove from group |
| GET | /api/nodes/:id/agent-info |
Get CC Agent info (Xray) |
| POST | /api/nodes/:id/generate-xray-keys |
Generate Reality keys |
| Method | Endpoint | Scope | Description |
|---|---|---|---|
| GET | /api/stats |
stats:read |
Panel statistics |
| GET | /api/groups |
stats:read |
List server groups |
| POST | /api/sync |
sync:write |
Sync all nodes |
| POST | /api/kick/:userId |
sync:write |
Kick user from all nodes |
Send real-time event notifications to any HTTP endpoint.
Configure: Settings β Security β Webhooks
POST https://your-endpoint.com/webhook
Content-Type: application/json
X-Webhook-Event: user.created
X-Webhook-Timestamp: 1700000000
X-Webhook-Signature: sha256=<hmac>
User-Agent: C3-Celerity-Webhook/1.0
{
"event": "user.created",
"timestamp": "2024-01-01T00:00:00.000Z",
"data": { ... }
}const crypto = require('crypto');
const expected = 'sha256=' + crypto
.createHmac('sha256', YOUR_SECRET)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
// compare with X-Webhook-Signature header| Event | Trigger |
|---|---|
user.created |
User created |
user.updated |
User updated |
user.deleted |
User deleted |
user.enabled |
User enabled |
user.disabled |
User disabled |
user.traffic_exceeded |
User traffic limit reached |
user.expired |
User subscription expired |
node.online |
Node came online |
node.offline |
Node went offline |
node.error |
Node error |
sync.completed |
Sync cycle finished |
| Field | Type | Description |
|---|---|---|
userId |
String | Unique ID |
username |
String | Display name |
subscriptionToken |
String | URL token for subscription |
xrayUuid |
String | UUID for Xray VLESS (auto-generated) |
enabled |
Boolean | User active status |
groups |
[ObjectId] | Server groups |
nodes |
[ObjectId] | Direct node assignments |
traffic |
Object | { tx, rx, lastUpdate } β used traffic |
trafficLimit |
Number | Traffic limit in bytes (0 = unlimited) |
maxDevices |
Number | Device limit (0 = group limit, -1 = unlimited) |
expireAt |
Date | Expiration date |
| Field | Type | Description |
|---|---|---|
type |
String | hysteria or xray |
name |
String | Display name |
flag |
String | Country flag (emoji) |
ip |
String | IP address |
domain |
String | Domain for SNI/ACME |
sni |
String | Custom SNI for masquerading |
port |
Number | Main port (443) |
portRange |
String | Port hopping range |
portConfigs |
Array | Multi-port: [{ name, port, portRange, enabled }] |
obfs |
Object | Obfuscation: { type: 'salamander', password } |
statsPort |
Number | Hysteria stats port (9999) |
statsSecret |
String | Stats API secret |
groups |
[ObjectId] | Server groups |
outbounds |
Array | Proxies for ACL: [{ name, type, addr }] |
aclRules |
[String] | ACL rules |
maxOnlineUsers |
Number | Max online for load balancing |
rankingCoefficient |
Number | Sorting coefficient (1.0) |
status |
String | online/offline/error/syncing |
traffic |
Object | { tx, rx, lastUpdate } β node traffic |
xray |
Object | Xray settings (see below) |
| Field | Type | Description |
|---|---|---|
transport |
String | tcp, ws, grpc, xhttp |
security |
String | reality, tls, none |
flow |
String | xtls-rprx-vision (for tcp) |
fingerprint |
String | chrome, firefox, safari, etc. |
alpn |
[String] | ALPN protocols (h3, h2, http/1.1) |
realityDest |
String | Disguise destination |
realitySni |
[String] | Server names |
realityPrivateKey |
String | x25519 private key |
realityPublicKey |
String | Public key |
realityShortIds |
[String] | Short IDs |
realitySpiderX |
String | Spider X path (default: /) |
wsPath |
String | WebSocket path |
wsHost |
String | WebSocket host header |
grpcServiceName |
String | gRPC service name |
xhttpPath |
String | XHTTP path |
xhttpHost |
String | XHTTP host header |
xhttpMode |
String | auto, packet-up, stream-up |
apiPort |
Number | Xray gRPC API port (61000) |
inboundTag |
String | Inbound tag (vless-in) |
agentPort |
Number | CC Agent port (62080) |
agentToken |
String | Agent token |
agentTls |
Boolean | TLS for CC Agent |
| Field | Type | Description |
|---|---|---|
name |
String | Group name |
description |
String | Description |
color |
String | UI color (#hex) |
maxDevices |
Number | Device limit for group |
subscriptionTitle |
String | Title in subscription profile |
Control traffic routing on each Hysteria node. Access: Panel β Node β Traffic Filtering.
| Action | Description |
|---|---|
reject(...) |
Block connection |
direct(...) |
Allow through server |
reject(suffix:doubleclick.net) # Block ads
reject(suffix:googlesyndication.com)
reject(geoip:cn) # Block Chinese IPs
reject(geoip:private) # Block private IPs
direct(all) # Allow everything else
- Add proxy (e.g.,
my-proxy, SOCKS5,1.2.3.4:1080) - Use in rules:
my-proxy(geoip:ru)
Configure in Settings:
- Enable balancing β Sort nodes by current load
- Hide overloaded β Exclude nodes at capacity
Algorithm:
- Get user's nodes from groups
- Sort by load % (online/max)
- Filter overloaded if enabled
- Fall back to
rankingCoefficient
Priority:
- User's personal limit (
maxDevices > 0) - Minimum limit from user's groups
-1= unlimited
Device Grace Period β delay (in seconds) before counting a disconnected device, to avoid false triggers during reconnections.
Customize the HTML subscription page in Settings β Subscription:
| Field | Description |
|---|---|
Logo URL |
Logo URL for page header |
Page Title |
Page title |
Support URL |
Support link (button at bottom) |
Web Page URL |
Profile URL (profile-web-page-url header) |
The subscription page automatically shows:
- QR code for app import
- Traffic stats and expiration
- Location list with copy buttons
Configure in Settings β Backups:
- Interval (in hours)
- Number of local copies to keep
Dashboard button β file auto-downloads.
Upload .tar.gz archive via interface.
Backups can be automatically uploaded to S3-compatible storage (AWS S3, MinIO, Backblaze B2, Cloudflare R2, etc.).
Configure: Settings β Backups β S3
| Field | Description |
|---|---|
Endpoint |
Storage URL (for MinIO, etc.). Leave empty for AWS S3 |
Region |
Region (e.g., us-east-1) |
Bucket |
Bucket name |
Prefix |
Prefix/folder for backups |
Access Key ID |
Access key |
Secret Access Key |
Secret key |
Keep Last |
How many backups to keep in S3 |
Configuration examples:
# AWS S3
Endpoint: (empty)
Region: eu-central-1
Bucket: my-backups
# MinIO
Endpoint: https://minio.example.com
Region: us-east-1
Bucket: backups
# Cloudflare R2
Endpoint: https://<account-id>.r2.cloudflarestorage.com
Region: auto
Bucket: my-backupsversion: '3.8'
services:
mongo:
image: mongo:7
restart: always
volumes:
- mongo_data:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER:-hysteria}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
backend:
image: clickdevtech/hysteria-panel:latest
restart: always
depends_on:
- mongo
ports:
- "80:80"
- "443:443"
volumes:
- ./logs:/app/logs
- ./greenlock.d:/app/greenlock.d
- ./backups:/app/backups
env_file:
- .env
volumes:
mongo_data:| Variable | Required | Description |
|---|---|---|
PANEL_DOMAIN |
β | Panel domain |
ACME_EMAIL |
β | Let's Encrypt email |
ENCRYPTION_KEY |
β | SSH encryption key (32 chars) |
SESSION_SECRET |
β | Session secret |
MONGO_PASSWORD |
β | MongoDB password (for Docker) |
MONGO_USER |
β | MongoDB user (default: hysteria) |
MONGO_URI |
β | MongoDB connection URI (for non-Docker) |
REDIS_URL |
β | Redis URL for cache (default: in-memory) |
PANEL_IP_WHITELIST |
β | IP whitelist for panel |
SYNC_INTERVAL |
β | Sync interval in minutes (default: 2) |
API_DOCS_ENABLED |
β | Enable interactive API docs at /api/docs |
LOG_LEVEL |
β | Logging level (default: info) |
Pull requests welcome!
MIT