Stand up a single sing-box server from scratch — write a minimal
cheburbox.json, generate the runtime config, validate it in-process, and (optionally) print a share link. End to end in five steps.
- Prerequisites
- Step 1 — Create a project directory
- Step 2 — Write a minimal
cheburbox.json - Step 3 — Generate configs
- Step 4 — Validate the output
- Step 5 — Generate share links (optional)
- What's next
- The
cheburboxbinary on yourPATH. See Installation. - That's it. Cheburbox links sing-box as a Go library and runs
box.Newin-process during validation, so no externalsing-boxbinary is required to runcheburbox validate.
A cheburbox project is just a directory whose direct child directories each define one server.
$ mkdir my-project
$ cd my-projectThe current working directory is the project root by default. (You can also pass --project /path/to/root to any cheburbox command.)
Each server lives in its own subdirectory. Create one called home-server with a cheburbox.json inside:
$ mkdir home-serverWrite home-server/cheburbox.json:
{
"version": 1,
"endpoint": "1.2.3.4",
"dns": {
"servers": [
{ "type": "local", "tag": "dns-local" }
],
"final": "dns-local"
},
"inbounds": [
{
"type": "vless",
"tag": "vless-in",
"listen_port": 443,
"users": [
{ "name": "alice" }
]
}
],
"outbounds": [
{ "type": "direct", "tag": "direct" }
]
}What each block does:
version: 1— schema version. The current and only supported value is1. Omitting it (or setting it to0) fails withmissing or zero version field.endpoint: "1.2.3.4"— public IP or hostname of this server. Required wheneverinboundsis non-empty (purely-outbound "client" servers may omit it). Replace with your real public address.dns— at least one DNS server is mandatory. Here a singlelocalresolver taggeddns-localis selected as the default viafinal.inbounds— one VLESS listener on port443, with one user namedalice. Note:usersis an array of objects ([{ "name": "alice" }]), not strings. Cheburbox auto-generates the UUID for each declared user duringgenerateand persists it in the resultingconfig.json.outbounds— onedirectoutbound, used as the default egress for traffic this server proxies.
Gotcha — single-level discovery. Cheburbox only looks at direct child directories of the project root. A
cheburbox.jsonplaced at the root (next to your server folders) is ignored. Likewise, server folders nested two levels deep are not picked up.
From the project root, run:
$ cheburbox generateWhat happens:
- Discover. Cheburbox lists the immediate subdirectories of the current directory and picks the ones containing
cheburbox.json(or.cheburbox.jsonnet). - Resolve & build. For each server it loads the schema, validates it, and builds a sing-box
option.Optionsvalue in memory. Cross-server outbound references (none in this minimal example) would be sorted into a DAG and processed leaves-first. - Persist credentials. Cheburbox reads any pre-existing
<server>/config.jsonfrom a previous run and reuses its UUIDs, passwords, and Reality keys so existing clients keep working. On a fresh project there is nothing to read; new credentials are generated. - In-memory build, then write. All servers are built in memory first; files are written only after every server succeeds. A mid-write failure (e.g. disk full) can leave partial output — the write itself is not transactional.
For our single-server example the resulting tree is:
my-project/
└── home-server/
├── cheburbox.json
└── config.json
That home-server/config.json is the file you would feed to a real sing-box run --config home-server/config.json on the actual server. It contains the populated VLESS user with a freshly-generated UUID, xtls-rprx-vision flow, the DNS section, and the direct outbound.
Other potential outputs (not produced by this minimal example, but worth knowing about):
home-server/certs/<server_name>.crtand<server_name>.key— only generated for Hysteria2 inbounds with atls.server_name.home-server/rule-set/<name>.srs— only generated whenroute.custom_rule_setslists local rule-set sources.
For the full flag list (--full-reset, --orphan, --server, --dry-run), see Generate.
$ cheburbox validatevalidate runs in two phases:
- Phase 1 — schema and cross-server invariants: required fields, port ranges, cross-server outbound targets resolve, no DAG cycles, no two Hysteria2 inbounds sharing a
tls.server_nameon the same server, AmneziaWG inbound/outbound constraints (CIDR, protocol, target resolution), and urltest/selector member tags exist on the same server. - Phase 2 — sing-box parsing: cheburbox calls
box.Newon each<server>/config.jsonin-process. This catches anything sing-box itself would reject at startup (unknown option keys, malformed pass-throughroute.rules, etc.).
Tip. If you run
cheburbox validatebefore you've ever runcheburbox generate, Phase 2 emits a Warning per server (skipped sing-box check: <server>/config.json not found) and reports the server as PASS. The warning is informational, not a failure — Phase 1 still runs.
For more detail on each check and how to interpret reports, see Validate.
Once cheburbox generate has produced home-server/config.json, you can print user share links for VLESS and Hysteria2 inbounds:
$ cheburbox links --format uriThis emits one URI per (server, inbound, user) tuple to stdout. For our example, you'll get a single vless://...#home-server-vless-in-alice line.
Other useful flags (full list in Links):
--format json— emit sing-box-style outbound JSON objects instead of share URIs.--server home-server— limit output to one server.--user alice— limit to one user.
Gotcha.
linksreads the persisted credentials from<server>/config.json. If you haven't rungenerateyet, no credentials exist andlinksproduces empty output (no error). Onlyvlessandhysteria2inbounds produce links; other types (e.g.tun) are silently skipped.
- Configuration — full schema reference for
cheburbox.json(every field on every inbound, outbound, DNS, route, Reality, Hysteria2 obfs/masquerade, experimental cache, etc.). - Architecture — how the two type layers, the cross-server DAG, credential persistence, and the two-pass generation model fit together.