Native Deployment
Applies to: ChengOS v0.2.0+ | Last verified: 2026-09-25 | Source:
deploy/hybrid/start.sh,deploy/hybrid/status.sh,deploy/hybrid/stop.sh,deploy/chengos.sh,deploy/.env.example
Native — "hybrid" in the scripts — runs the compiled cheng-api binary directly on the host,
with no container runtime. It is the mode to choose when you want the lowest overhead, or when
you have no root on the machine at all.
Deployment Modes compares it with Docker and distributed; this page is how to run it.
Install
./chengos.sh install --mode native --db-install-mode managed-process --with api,ui,app,cli
./chengos.sh start
./chengos.sh status
chengos.sh also runs as an interactive menu with no arguments, in English or Chinese. The
non-interactive commands are install, start, stop, restart, status, version,
update, rollback, doctor, cloudflare, and uninstall.
Modules are selected with --with: api, ui, app, cli, plus redis and qdrant for the
optional infrastructure. The installer offers full, minimal (Postgres + API + UI), or custom.
status exits non-zero when the API is not serving, deliberately, so it works as a health
probe in a monitoring script.
The database decision
DB_INSTALL_MODE is the choice that shapes everything else:
| Mode | Databases run as | Needs sudo |
|---|---|---|
managed-process (default) | Unprivileged user processes inside the install directory | No |
system-service | System packages plus systemd services | Yes |
managed-process creates a PostgreSQL data directory with initdb inside the install tree
and starts it with pg_ctl on port 5432, as your own user. Nothing touches the system package
manager and nothing needs elevation. This is what makes ChengOS installable on a shared host or
a locked-down VM.
system-service installs the packages and enables them with
systemctl enable --now postgresql (and valkey-server when Redis is enabled), so the databases
start with the machine. start.sh checks for sudo up front rather than failing halfway.
Switching from system-service to managed-process stops and disables the system services
first, so the two do not both try to hold port 5432.
If initdb and postgres are not on the host at all, the script fails with an explicit message
rather than guessing — install the Postgres client tools and rerun.
Override per run:
./chengos.sh start --db-install-mode system-service
Configuration
Everything reads deploy/.env. start.sh copies .env.example to .env on the first run and
then stops, telling you to edit it — it will not start with placeholder secrets.
For native mode the essentials are:
DATABASE_URL=postgres://tianai_db:…@127.0.0.1:5432/master_router
REDIS_URL=redis://:…@127.0.0.1:6379
QDRANT_URL=http://127.0.0.1:6334
DB_INSTALL_MODE=managed-process
ENABLE_REDIS=false
ENABLE_QDRANT=false
PORT=19225
API_PORT=19225
UI_PORT=8080
APP_PORT=5055
BIND_ADDRESS=127.0.0.1 # the API: this host only
UI_BIND=0.0.0.0 # the UI server: narrow this before going public
APP_BIND=0.0.0.0 # the app server: likewise
CREDENTIAL_MASTER_KEY_1=<64 hex chars> # openssl rand -hex 32
JWT_SECRET=<64 hex chars>
Hosts are 127.0.0.1 in native mode, not the compose service names used in Docker. That is
the most common copy-paste error between modes. See
Storage, Redis & Vector Configuration.
ENABLE_REDIS and ENABLE_QDRANT default to false: a minimal install runs on PostgreSQL
alone. Turn on Redis when you need scheduling or agent memory,
and Qdrant when you need RAG.
deploy/generate-env.sh produces a .env with generated secrets instead of placeholders.
The hardened topology
A native host runs three services that are not equally public:
| Service | Listens on | Reached by |
|---|---|---|
cheng-api | BIND_ADDRESS = 127.0.0.1 | The UI and app servers, which proxy /api and /ws; the CLI and health checks on this host |
ui-server | UI_BIND | Your reverse proxy or cloudflared |
app-server | APP_BIND | Your reverse proxy or cloudflared |
The API never needs another address: even a remote CLI or desktop client should reach it
through the proxy's 443, where /api is forwarded. UI_BIND and APP_BIND default to
0.0.0.0, so a fresh install serves the frontends on every interface until you change them:
- Proxy or
cloudflaredon this host — set both to127.0.0.1. - Proxy on another machine — set both to this host's private or VPN address (another
machine's address cannot be bound), list the proxy in
TRUSTED_PROXY_IPSunless it is on an RFC 1918 address, and make sure the proxy-to-origin hop is private or encrypted.
.env is never overwritten by an upgrade, so an older installation keeps BIND_ADDRESS=0.0.0.0
and may lack UI_BIND / APP_BIND entirely until you add them. The frontends print their
effective proxy trust set at startup. The full procedure, for both the tunnel and the proxy
routes, is in Hardening a Public Deployment.
Optional: a local decision model. With LAYA_ENABLED=true and LAYA_BIN plus
LAYA_MODEL_PATH or LAYA_MODELS_DIR set, start.sh also launches laya serve on
LAYA_PORT (1215), verifies each model file against its pinned SHA-256, and warms it up;
status and stop include it. See
Decision Providers & TypeSafe Jev.
Layout
start.sh creates and uses:
<install-root>/
bin/cheng-api the binary
.env
logs/ cheng-api.log, ui-server.log, app-server.log
runtime/ pid files, database data directory
skills/ skill packages
ui/ app/ served frontend assets
Log paths can be overridden with CHENG_API_LOG_FILE, CHENG_UI_LOG_FILE, and
CHENG_APP_LOG_FILE.
Day-to-day operation
./chengos.sh start # start selected modules
./chengos.sh status # process status and version, non-zero if the API is down
./chengos.sh restart # stop then start with the recorded module set
./chengos.sh stop # stop services
./chengos.sh version
./chengos.sh update # scripts and package
./chengos.sh update --scripts-only # scripts only
./chengos.sh doctor # read-only exposure report; non-zero on a problem
./chengos.sh uninstall
stop takes an option controlling whether the databases stop with the application — leave them
running if something else uses them.
status reports the installed version and whether a newer release is available; the update flow
is covered in Upgrades & Rollback.
The scripts underneath live in deploy/hybrid/ (start.sh, stop.sh, status.sh,
generate-env.sh) and can be called directly, for example
bash hybrid/start.sh --with api,ui. Prefer chengos.sh: it records the installed mode and
module set so restart and update do the right thing without repeating the flags.
Installing from source
If the install root contains chengflow/ and chengflow-ui/, the installer detects a developer
workspace and compiles from source instead of unpacking a release archive. On a server you
almost certainly want the release package; on a development machine this is the convenience.
Troubleshooting
| Symptom | Cause |
|---|---|
No .env file found | First run created it; edit it and rerun |
| Postgres will not start | Port 5432 already held — often a leftover system-service install |
Postgres tools are not installed | initdb/postgres missing; install the client tools |
status says not serving, no log | Check logs/cheng-api.log; usually a bad DATABASE_URL or a missing secret |
| Scheduling does nothing | ENABLE_REDIS=false |
| RAG nodes fail to connect | ENABLE_QDRANT=false, or QDRANT_URL pointing at 6333 instead of 6334 |
system-service install fails early | It requires sudo and checks up front |
| Remote CLI cannot connect to port 19225 | Expected: the API is loopback-only. Connect through the reverse proxy on 443 |
status reports UI NOT RESPONDING | It prints the address it probed; if that differs from UI_BIND, .env changed without a restart |
EADDRNOTAVAIL on start | UI_BIND / APP_BIND names an address that is not on this host |
Next steps
- Deployment Modes — how this compares with Docker.
- Environment Variable Reference — every variable.
- Upgrades & Rollback — keeping it current.
- Hardening a Public Deployment — putting it on the internet.