Checked on 2026-10-22.
Deploy, package and distribute sing-box.
The primary deployment target is a single VPS that runs:
- one
sing-box.servicewith Trojan, Hysteria2 and optional Naive and Snell v6 inbounds - the authenticated download portal behind
nginx certbot-nginxfor CA-signed certificates
One Django/PostgreSQL service provides the portal and control plane. It manages
operators, proxy accounts (Account), and protocol credentials (Credential).
Users open their subscription pages. Operators manage accounts and traffic through
SSH-tunnelled Django Admin. There is no separate public password-login portal.
See the control-plane guide for enablement and recovery.
The public GitHub repository is xiaosq2000/sing-box-manager; the Go module
uses the matching path. The project and its commands remain sing-box-manager,
sbm, and sbc.
This repository contains code, example configuration, and synthetic test fixtures. It does not include a runtime inventory or generated releases. Keep deployment inventory, credentials, TLS keys, signing keys, and user data in an operator-owned location or a separate private repository. See secret handling and contributing before adding configuration or test data.
CI uses standard GitHub-hosted runners on Linux, macOS, and Windows. Do not route untrusted public pull requests to a persistent self-hosted runner.
The build and deploy environment targets Linux x86-64. Install Pixi and run commands from the repository root.
The code-built configurations target sing-box 1.14.3. Pixi provides Python 3.12
and Go 1.25.5 or later in the 1.25 series for custom server builds and the sbc
client.
pixi installCopy .sops.example.yaml to the ignored .sops.yaml and set your own public Age
recipient. Create or edit your encrypted inventory using the
secret-handling guide. Copying the
example to a .sops.yaml filename does not encrypt it. For local development, use
a plaintext .yaml outside the checkout and pass its path with --config.
This repo tracks bundled font files with Git LFS. If your clone was created on a host that does not already have Git LFS configured, run this once per checkout before starting the app or building releases:
git lfs install --local
git lfs pullgit lfs install --local stores the filter configuration in .git/config instead of
your user-level ~/.gitconfig.
This applies to the machine you build and deploy from. The server needs neither git
nor Git LFS: sbm deploy ships the real font bytes over rsync, and refuses to start
if this clone still holds LFS pointer stubs.
See config/inventory/example.yaml for the full
config shape. Select infrastructure settings and legacy inventory with --config <path>
or SBM_CONFIG. In authoritative mode, PostgreSQL owns proxy accounts and credentials.
Manage them in Django Admin, not in the inventory.
deployment.client_region optionally sets the time zone and language clients
present for the node; see
the client guide. Clients opt in with
sbc privacy region on for one account or sbc privacy region on --system for
the whole machine. sbc privacy browser language on separately matches Linux
Chrome's language preferences without changing the system locale. See
Chrome language matching.
sbc privacy browser geolocation on separately blocks website location requests
with a system-wide Linux Chrome policy. IP-based location estimates remain
possible. See geolocation blocking.
On native Linux, sbc tun on runs a root-owned TUN service instead of mixed
mode. It needs systemd, /dev/net/tun, and sudo, and it refuses remote SSH
sessions. sbc route and sbc protocol control active TUN without restarting
capture. sbc ip, sbc check, and sbc speed download diagnose the active
mixed or TUN path without changing selections. See
network diagnostics.
sbm serve runs Django on loopback at control_plane.port (default 47071).
It requires control_plane.enabled and control_plane.secret_key.
Retain web.session_secret for installed legacy clients' machine tokens.
See server configuration.
web.subscription_secret keys the subscription links the sbc client fetches its
config from. Without it, sbm serve turns those links off.
The Trojan, Hysteria2, Naive and Snell renderers build configurations in code and keep real
deployment secrets in an operator-owned encrypted runtime inventory. See
docs/SECRET_HANDLING.md before editing any user
credentials, obfs secrets, or TLS values.
default_protocol controls the initial protocol for new client installs and mobile
profile imports. When omitted, it prefers Snell if its infrastructure is configured,
or Trojan otherwise. An explicit value overrides this default. Explicit Snell requires
both deployment.snell_port and snell.psk. Subscriptions use the account's first
enabled protocol when it lacks the configured default. This is not connection-failure
fallback. Install parameters and saved client selections take precedence. Saved
choices survive refresh, upgrade and restart. A client without a saved choice
follows the new default, even if it is already installed.
The server installer enables all packaged protocol inbounds by default. A Naive inbound is omitted when its roster is empty or entirely disabled.
Snell v6 uses deployment.snell_port, a shared snell.psk, and independent
PostgreSQL userkeys. No enabled users means no Snell listener. It supports all
existing client platforms without changing routing rules.
See Snell setup and acceptance, including the explicit existing-account
backfill and device-test limits.
If traffic_stats.enabled is turned on in the config file, sbm release builds a
custom Linux server sing-box binary from the upstream source tag with
with_v2ray_api, while client download archives continue using the official upstream
release assets.
Desktop and mobile profiles offer china-direct (the default), gfw-proxy,
ai-only, global, and direct routes. china-direct proxies unknown destinations.
gfw-proxy proxies GFWList and AI destinations and sends other traffic directly.
direct sends all allowed application traffic and its DNS directly, including AI.
Desktop configs retain china, gfw, and ai as aliases. Mobile shows only the
five canonical names. See the
CLI guide for route descriptions and diagnostics. Every client profile keeps the exact
deployment.host and optional deployment.direct_domain_suffixes direct, with
matching direct DNS rules. Suffixes cover the apex and subdomains.
Desktop and mobile proxy strategies send community-listed Anthropic and AI
traffic through the proxy before China-direct rules. direct sends these
connections and their DNS directly. The dedicated Anthropic set and broader
AI set replace local domain maintenance. See
community AI rules for sources and
coverage limits.
iOS and Android use one unified remote profile with all enabled account protocols behind a selector. The portal provides one import button and QR code. Per-protocol mobile URLs are not supported. Reimport from the subscription page. Mobile DNS returns real answers, without FakeIP. See the mobile guide for migration and device acceptance.
The sbc downloads include initial rule files, so the proxy
can start before rule downloads succeed. DNS answers persist across service restarts,
with normal DNS expiry. See the
release guide for build requirements.
sbc enables automatic configuration refresh and signed binary upgrades by default,
with separate 6h intervals. Active Linux TUN only checks and reports updates.
Manual sbc refresh or sbc upgrade republishes TUN under its current policy.
Use sbc refresh auto off or sbc upgrade auto off to
disable either policy. See automatic maintenance
for intervals, saved preferences, and migration from older clients.
sbc lang saves the language of sbc messages as en, zh, or auto;
SBC_LANG overrides it. See CLI language.
sbm release can also read a repo-root .env for local operator secrets such as
SOPS_AGE_KEY and GITHUB_TOKEN; the secret-handling doc covers the tradeoffs.
sbm release uses the current files in config/ as-is; it does not pin, reset,
or check out config state with git.
docs/SNELL.md: Snell v6 settings, userkey backfill, rollout and acceptancedocs/DEVELOPMENT.md: focused checks, docstale, and agent instruction maintenancedocs/SECRET_HANDLING.md: inventory creation, credentials, and TLS handlingdocs/VPS_DEPLOYMENT.md: primary end-to-end VPS deployment guidedocs/RELEASE_AND_PORTAL.md: build releases, run the portal, and download client packagesdocs/README_SBC.md: client installation, legacy migration, commands, help, and shell completiondocs/CLIENT_DIAGNOSTICS.md: mixed/TUN diagnostic contracts, capture limits, and acceptance gatesdocs/WEBRTC.md: Linux Chrome WebRTC protection, geolocation blocking, language matching and recoverydocs/README_WIN.md: Windows installer, scheduled-task layout, and uninstalldocs/README_MOBILE.md: official iOS/Android import links, local QR codes, and device acceptancedocs/REMOTE_DEPLOYMENT.md: sync the active release artifacts to a remote host and install themdocs/CONTROL_PLANE.md: Django portal, PostgreSQL accounts, private admin, and recoverydocs/REFACTOR_SPEC.md: target architecture and refactoring milestones
Every sbm operation requires --config <path> or exported SBM_CONFIG. Put the
global --config option before the subcommand. --help works without a config.
Use the same inventory for a release build and its deploy.
When the control plane is enabled, deployment installs /usr/local/bin/sbm on
the VPS. This wrapper sets SBM_CONFIG to the deployed runtime inventory if unset,
changes to the deployment root, and disables Python bytecode writes.
An explicit --config still overrides SBM_CONFIG.
Build release artifacts:
pixi run sbm --config config/inventory/runtime.sops.yaml releaseStart Django after database migration and static-file collection:
pixi run sbm --config config/inventory/runtime.sops.yaml serveCollect per-user traffic stats from the local sing-box node service:
pixi run sbm --config config/inventory/runtime.sops.yaml stats-collectCollect the per-domain breakdown. This subscribes to the sing-box 1.14.0 API service and runs until stopped, because sing-box drops connection events while nobody is listening:
pixi run sbm --config config/inventory/runtime.sops.yaml stats-streamDjango operators with control_plane.view_traffic can open /admin/traffic/
and /admin/traffic/connections/ through an SSH tunnel. This permission does not
permit account edits. The legacy inventory admin flag grants no Django permissions.
After building a release, deploy to a fresh VPS. Replace vpn-host with your SSH
host alias:
pixi run sbm --config config/inventory/runtime.sops.yaml deploy --hostname vpn-host --bootstrapDeploy subsequent releases to an already-provisioned host:
pixi run sbm --config config/inventory/runtime.sops.yaml deploy --hostname vpn-hostDeploy uses rsync over SSH, and Pixi provides the local rsync binary. It ships
the code and existing release; it does not build a release. See the
deploy options for protocol selection, custom
paths, bootstrap email, and dry runs.
Manage the control plane on the VPS after deployment:
sudo -u sbm sbm control-plane migrate
sudo -u sbm sbm control-plane createsuperuser
# Initial import only, before enabling authoritative mode:
sudo -u sbm sbm control-plane import
sudo sbm control-plane syncFor import restrictions, account switches, and deployment modes, see the control-plane guide.
The full action names create-admin, import-inventory, and sync-node also work.
Other actions are collect-static, serve, health, check, and backfill-snell.
Only backfill-snell accepts --dry-run.
Omitting the action runs check. Existing control-plane --action ACTION commands
remain supported. Use one action form per command.
From a workstation, both concise and full action names support remote execution:
pixi run sbm --config /path/to/runtime.sops.yaml control-plane sync --remote vpn-host --remote-root /opt/sing-box-managerSee the control-plane guide for permissions and recovery.
pixi install
pixi run test tests/test_cli.pySee DEVELOPMENT.md for checks appropriate to the change.