# Docker Deployment

> Applies to: ChengOS v0.1.0+ ｜ Last verified: 2026-08-12 ｜ Source: `deploy/README.md`, `deploy/docker/`, `deploy/.env.example`

Docker mode runs every ChengOS service as a container under Docker Compose. It is the
recommended production deployment: the release is pinned to exact image tags, the
datastores are bound to loopback by default, and updates and rollbacks swap whole images
rather than binaries on a host.

## Prerequisites

- Docker Engine with the Compose plugin (`docker compose version` must work)
- A user in the `docker` group, or `sudo` for the Docker commands
- Ports `8080` and `5055` free on the host, plus `3000` on loopback

## Install and start

From the `deploy/` directory:

```bash
./chengos.sh install --mode docker --with api,ui,app,cli
./chengos.sh start
```

The installer writes `deploy/.env`, generates the secrets that must not be left at their
template values, and brings up the Compose project. Use `./chengos.sh stop` to bring it
down and `./chengos.sh status` to inspect containers, ports, and live logs.

## Pin the release

All four ChengOS images resolve their tag from one value in `deploy/.env`:

```bash
CHENGOS_VERSION=0.1.0
```

Because every image reads the same value, a Docker installation can never mix images from
unrelated releases. `latest` is a first-install convenience only — it is **not a
reproducible production version** and is never an update or rollback target. Pin an exact
release before the installation carries anything you care about.

Per-service overrides exist for private registries and local development builds:

```bash
CHENGOS_API_IMAGE=registry.example.com/chengos/chengos:0.1.0
CHENGOS_UI_IMAGE=registry.example.com/chengos/chengos_ui:0.1.0
CHENGOS_APP_IMAGE=registry.example.com/chengos/chengos_app:0.1.0
CHENGOS_CLI_IMAGE=registry.example.com/chengos/chengos_cli:0.1.0
```

An override beats `CHENGOS_VERSION` for that one service. Be aware of the trade-off: while
**any** override is set, coordinated Docker release updates are disabled, and
`chengos.sh status` says so. Remove the overrides before you rely on `chengos.sh update`.

## Connection strings differ from native mode

This is the single most common mistake when moving a `.env` between modes. Hostnames in
`DATABASE_URL`, `REDIS_URL`, and `QDRANT_URL` depend on where the datastores actually run:

| Deployment | Hostname to use |
|---|---|
| Native / `managed-process` | `127.0.0.1` |
| Docker | `postgres`, `redis`, `qdrant` (the Compose service names) |
| Distributed | Your external database host |

Inside the Compose network, containers reach each other by service name. A `DATABASE_URL`
pointing at `127.0.0.1` inside the API container resolves to the container itself, not to
the database.

## Networking and exposure

Compose already binds the API, PostgreSQL, Valkey, and Qdrant host ports to `127.0.0.1`, so
they are not reachable from outside the machine. Publish only your reverse proxy's `443` to
the network and terminate TLS there.

One trap deserves its own warning: **do not set `BIND_ADDRESS=127.0.0.1` inside the API
container.** Loopback inside a container means that container only, so the UI container
would no longer be able to reach the API over the Compose network. The correct value inside
Docker is `0.0.0.0`; isolation comes from the host port binding, not from the process bind
address.

## Public read-only demo mode

To expose an installation publicly as a read-only demo, set `CHENG_DEMO_MODE=true`. This is
a fail-closed allowlist: only health checks, login/refresh/validate, workspace discovery,
and workflow and node-definition reads stay available. Workflow saves and deletes,
execution over REST and WebSocket, registration, password changes, credentials, files,
documents, channels, MCP, and the scheduler all return `403 DEMO_MODE_RESTRICTED`.

Demo mode cannot bootstrap itself, because creating the demo user is one of the operations
it blocks. Initialize first, then switch:

```bash
# 1. Start normally on a trusted network
#    deploy/.env: CHENG_DEMO_MODE=false
./chengos.sh start
#    Create the fixed demo user and its default workspace through the UI
./chengos.sh stop

# 2. Set CHENG_DEMO_MODE=true in deploy/.env, then start again
./chengos.sh start
```

While demo mode is on, `RUN_MIGRATIONS` is ignored and the background workers do not
start — scheduler, MCP recovery, channel polling and streaming, OAuth token refresh, code
indexing, the file watcher, and cleanup jobs are all suppressed. To migrate, reset, or
update demo data, remove the instance from public access, disable demo mode, do the
maintenance, and re-enable it.

## Updating and rolling back

`chengos.sh update` is upgrade-only and verifies before it touches a running system: it
checks the SHA-256 checksum and signature, matches the archive's embedded `VERSION` against
the selected release, stops the services, and swaps the images. The version record is
written only after a post-update health check passes; a failure restores the previous
package automatically.

`chengos.sh rollback` restores the most recent backed-up release and re-runs start plus
health validation. It refuses when the installed release declares an irreversible database
migration — one that drops or rewrites data the previous release cannot read — and prints
the manual recovery procedure instead.

## Next steps

- [Environment variable reference](/docs/config-env-reference) — every value you may need
  to set, and which ones are mandatory.
- [Install ChengOS](/docs/install-chengos) — native mode, and the CLI-only install.
