English · فارسی · Tiếng Việt · 简体中文 · 繁體中文 · Русский
2S-UI is an open-source management panel for sing-box. It gives you a clean, multilingual interface for deploying, configuring and monitoring a wide range of proxy and VPN protocols — from a single VPS to a multi-node deployment.
2S-UI began as a fork of s-ui. The frontend is rewritten in full, with a range of features added on top to make the panel nicer to use.
Disclaimer: This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment
- Multi-protocol — VLESS, VMess, Trojan, Shadowsocks, Hysteria2, TUIC, AnyTLS and more, inbound and outbound, plus WireGuard, WARP and Tailscale endpoints (full list).
- Central TLS — Reality, uTLS fingerprints, XTLS, and certificates registered once then picked per inbound.
- Routing rules — matching on domain, IP, port, protocol, process, user and rule-set, combined with and/or, with a separate rule list for DNS.
- Client management — traffic quota, expiry date, IP limit, live online status, plus one-click share links, QR codes and subscriptions.
- Traffic statistics — per inbound, per client and per outbound, with reset controls.
- Subscriptions —
link,jsonandclashformats, usage and expiry reported back to the client app, external links folded in. - Multi-node cluster — node health monitoring, users shared across nodes, every node's servers merged into one subscription (details).
- Automatic HTTPS — Let's Encrypt issuance and renewal, plus an automatic nginx reverse proxy (details).
- One-click updates — in-place upgrades from the panel, checksum-verified.
- Rebuilt interface — a from-scratch frontend, hand-built components, dark and light themes, six languages including RTL.
Supported protocols
- General: Mixed, SOCKS, HTTP/HTTPS, Direct, Tun, Redirect, TProxy
- V2Ray based: VLESS, VMess, Trojan, Shadowsocks (incl.
plugin/plugin_opts) - Other protocols: ShadowTLS, Hysteria, Hysteria2, Naive¹, TUIC, AnyTLS
- Outbound only: Tor, SSH, Selector, URLTest
- Endpoints: WireGuard, WARP, Tailscale — with a latency test per endpoint or for all at once
- XTLS is supported, and Hysteria port hopping is available on the outbound form
1 Naive needs the cronet toolchain, which does not build everywhere: official Linux releases ship it on amd64, arm64, armv7 and 386 only. On armv6, armv5 and s390x a Naive outbound reports that the binary was built without it.
English · Farsi · Vietnamese · Chinese (Simplified) · Chinese (Traditional) · Russian
| Platform | Architecture | Status |
|---|---|---|
| Linux | amd64, arm64, armv7, armv6, armv5, 386, s390x | ✅ Supported |
| Windows | amd64, 386, arm64 | ✅ Supported |
| macOS | amd64, arm64 | 🚧 Experimental |
| Default | |
|---|---|
| Panel | port 2095, path /app/ |
| Subscription | port 2096, path /sub/ |
| User / password | admin / admin |
bash <(curl -Ls https://raw.githubusercontent.com/shenaba/2s-ui/main/install.sh)The language follows $LANG, or pass en, fa, ru, vi, zhcn or
zhtw:
SUI_LANG=zhcn bash <(curl -Ls https://raw.githubusercontent.com/shenaba/2s-ui/main/install.sh)Alpine ships neither bash nor curl. Add them first; the installer then
detects Alpine and sets the panel up as an OpenRC service:
apk add bash curl
bash <(curl -Ls https://raw.githubusercontent.com/shenaba/2s-ui/main/install.sh)- Download the latest Windows release from GitHub Releases
- Extract the ZIP file
- Run
install-windows.batas Administrator - Follow the installation wizard
- Access the panel at http://localhost:2095/app
mkdir 2s-ui && cd 2s-ui
wget -q https://raw.githubusercontent.com/shenaba/2s-ui/main/docker-compose.yml
docker compose up -dWithout compose, or building your own image
If Docker itself is not installed yet:
curl -fsSL https://get.docker.com | shPlain docker run:
mkdir 2s-ui && cd 2s-ui
docker run -itd \
-p 2095:2095 -p 2096:2096 -p 443:443 \
-v $PWD/db/:/app/db/ \
-v $PWD/cert/:/root/cert/ \
--name s-ui --restart=unless-stopped \
ghcr.io/shenaba/2s-ui:latestBuild your own image:
git clone https://github.com/shenaba/2s-ui
docker build -t 2s-ui .A specific version, manual installation, uninstall
A specific version. Add the version to the end of the installation command. e.g. v1.5.5:
VERSION=v1.5.5 && bash <(curl -Ls https://raw.githubusercontent.com/shenaba/2s-ui/$VERSION/install.sh) $VERSIONManual installation — Linux/macOS
- Get the latest version of 2S-UI based on your OS/Architecture from GitHub: https://github.com/shenaba/2s-ui/releases/latest
- OPTIONAL Get the latest version of
s-ui.shhttps://raw.githubusercontent.com/shenaba/2s-ui/main/s-ui.sh - OPTIONAL Copy
s-ui.shto/usr/bin/s-uiand runchmod +x /usr/bin/s-ui. - Extract s-ui tar.gz file to a directory of your choice and navigate to the directory where you extracted the tar.gz file.
- Copy *.service files to /etc/systemd/system/ and run
systemctl daemon-reload. - Enable autostart and start 2S-UI service using
systemctl enable s-ui --now - Start sing-box service using
systemctl enable sing-box --now
Manual installation — Windows
- Get the latest Windows version from GitHub: https://github.com/shenaba/2s-ui/releases/latest
- Download the appropriate Windows package (e.g.,
s-ui-windows-amd64.zip) - Extract the ZIP file to a directory of your choice
- Run
install-windows.batas Administrator - Follow the installation wizard
- Access the panel at http://localhost:2095/app
Uninstall — systemd
sudo -i
systemctl disable s-ui --now
rm -f /etc/systemd/system/sing-box.service
systemctl daemon-reload
rm -fr /usr/local/s-ui
rm /usr/bin/s-uiUninstall — OpenRC (Alpine)
sudo -i
rc-service s-ui stop
rc-update del s-ui default
rm -f /etc/init.d/s-ui
rm -fr /usr/local/s-ui
rm /usr/bin/s-uiNew releases are flagged on the version pill in the sidebar — the check is
client-side, so the panel host itself does not need to reach GitHub. On Linux
(systemd or Docker) one click upgrades in place: the panel downloads the
release, verifies it against the published SHA256SUMS, smoke-tests the new
binary, then replaces it and restarts. No SSH.
A running
.execannot replace itself, so on Windows the pill only links to the release page. In Docker the new binary lives in the container's writable layer: it survivesdocker restart, but recreating the container reverts to the image's version — pull a new image to make it stick.
One panel can manage the others. Add a remote 2S-UI instance on the Nodes page with its address and an API token, and the master will:
- Monitor it — a 5-second heartbeat reports each node as online, offline, or core-stopped (panel reachable but sing-box down).
- Share users with it — clients on the master that reference a node's
inbounds are pushed to that node and kept in sync, with each node's traffic
folded back into the master's counters. Sync is scoped to a
@clustergroup, so a node's own local users are never touched. - Fold its servers into one subscription — a client's subscription link carries the master's servers and every bound node's servers together.
A node is just another 2S-UI instance talking over the v2 API (Token header):
no agent to install, and the only node-side setup is creating that API token in
its own panel, so existing panels can be adopted as they are. Inbounds adopted
from a node become read-only replicas on the master — edit them on the node they
belong to.
Driving node sync from the API
POST <panel path>apiv2/save (the panel path is /app/ by default, so
/app/apiv2/save) triggers the web UI's immediate node fanout only when the
request carries sync=true; without it, client/inbound changes still converge
through the hourly reconcile safety net.
Everything TLS lives in the Domains & Certificates tab of Panel Settings. Panel and subscription service each pick their own domain, and the certificate paths follow the domain you select — no file paths to copy around.
🔐 Automatic certificates (ACME / Let's Encrypt) — recommended. Enter a
domain, add an email, and press issue: 2S-UI obtains and auto-renews a free
Let's Encrypt certificate, and the panel becomes reachable at
https://<your-domain>:2095/app. Requires TCP port 80 reachable from the
internet (HTTP-01 challenge). ACME is Linux-only and is hidden on Windows.
How issuance works, and the Docker port-80 caveat
Issuance runs through acme.sh, which 2S-UI installs for you on first use
(along with socat, needed for standalone validation) and registers for
automatic renewal via acme.sh's own cron entry — there is nothing for you to
schedule.
The validation method defaults to auto — standalone when port 80 is free,
otherwise it borrows the running nginx, provisioning a minimal server_name
block under /etc/nginx/conf.d if one is missing. Pick standalone or
nginx explicitly if you would rather decide. Renewals hot-reload the
certificate; no restart needed.
To publish port 80 with Docker: uncomment the
80:80line indocker-compose.yml, or add-p 80:80todocker run. Certificates are stored under/root/cert/<domain>/asfullchain.pem/privkey.pemand survive restarts (the Docker volume above maps that path out). If the domain/port is misconfigured, 2S-UI falls back to HTTP.
Bring your own certificate
Certificates you manage yourself — a Cloudflare origin CA, a corporate CA, certbot output — can be registered in the same tab. 2S-UI verifies the files are readable, that the key matches the certificate, and that the certificate really covers the domain; the domain then becomes selectable on the Interface and Subscription tabs like any other. Registered certificates are included in database backups.
To issue one by hand with Certbot:
snap install core; snap refresh core
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
certbot certonly --standalone --register-unsafely-without-email --non-interactive --agree-tos -d <Your Domain Name>Then register the resulting fullchain.pem / privkey.pem under
Domains & Certificates.
Behind a reverse proxy
Turn on TLS terminated by a reverse proxy and 2S-UI writes the vhost for
you: /etc/nginx/conf.d/s-ui-proxy-<domain>.conf, pointed at the panel with the
right forwarding headers, checked with nginx -t, reloaded, and rolled back
with nginx's own error message if anything fails. The subscription server can
sit behind the same proxy.
See CONTRIBUTING.md for development setup, coding conventions, testing, and the pull request process.
Building and running from source
git clone https://github.com/shenaba/2s-ui
cd 2s-ui
./runSUI.shbuild.sh builds the frontend, copies it into web/html/ for //go:embed,
and builds the binary with the required build tags; runSUI.sh runs it on top
of that. Building by hand needs those same tags — see
CONTRIBUTING.md.
Environment variables
| Variable | Type | Default |
|---|---|---|
| SUI_LOG_LEVEL | "debug" | "info" | "warn" | "error" |
"info" |
| SUI_DEBUG | boolean |
false |
| SUI_DB_FOLDER | string |
"db" |
| SUI_BIN_FOLDER | string |
"bin" |
SUI_BIN_FOLDER is only read while migrating a database from the old
subprocess-based layout; sing-box is embedded in the binary and there is no
bin/ folder at runtime.
