> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usecadence.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy with Docker Compose

> One server, automatic HTTPS, bot computers on the same machine.

This guide runs Cadence on a single Linux VM with `infra/compose/docker-compose.prod.yml`. It adds the `infra/compose/docker-compose.prod.docker.yml` overlay so bot computers run as Docker containers on the same machine, with no outside computer provider.

## Before you start

* A Linux VM with Docker Engine 26 or newer and the Compose plugin.
* A domain name, for example `app.example.com`, with an `A` (or `AAAA`) record pointing at the VM.
* Inbound TCP 80 and 443, and UDP 443, open on the VM.
* At least one model account: an OpenRouter or Anthropic API key, or people can [connect their own](https://docs.usecadence.gg/bots/models.md) after sign-in.

> [!TIP]
>
> The repository ships `infra/compose/harden-host.sh`, which locks down SSH, the firewall and automatic security updates on a fresh Ubuntu host. Run it before you deploy.

**1. Clone the repository**

The default and supported location is `/srv/cadenceplus`.

```bash theme={null}
sudo git clone https://github.com/attentionfactory/cadence-plus.git /srv/cadenceplus
cd /srv/cadenceplus
```

If you clone it somewhere else, set `CADENCE_PLUS_DEPLOY_DIR` to that path in the next step.

**2. Create the .env file**

Copy `.env.example` to `.env` and set at least these values. Generate every secret fresh, and use a different value for each.

```env theme={null}
NODE_ENV=production
CADENCE_PLUS_HOST=app.example.com
BETTER_AUTH_URL=https://app.example.com
WEB_ORIGIN=https://app.example.com
API_URL=https://app.example.com

POSTGRES_PASSWORD=          # openssl rand -hex 16
POSTGRES_APP_PASSWORD=      # openssl rand -hex 16
BETTER_AUTH_SECRET=         # 32+ random characters
ENCRYPTION_KEY=             # 64 hex characters: openssl rand -hex 32
SCREEN_PROXY_SECRET=        # 32+ random characters
SANDBOX_SUPERVISOR_TOKEN=   # 32+ random characters

SIGNUPS_ENABLED=true
SIGNUP_ALLOWLIST=owner@example.com
OPENROUTER_API_KEY=
```

The `.env` file is excluded from Git and from the Docker build. Never commit it.

**3. Pick the browser for bot computers (optional)**

Bot computers use Chromium by default. To give them Google Chrome instead, add:

```env theme={null}
CADENCE_PLUS_COMPUTER_BROWSER=chrome
```

Google Chrome is available on amd64 servers.

**4. Build and start**

Pass both Compose files, in this order, on every command.

```bash theme={null}
docker compose --env-file .env \
  -f infra/compose/docker-compose.prod.yml \
  -f infra/compose/docker-compose.prod.docker.yml \
  pull --ignore-buildable

docker compose --env-file .env \
  -f infra/compose/docker-compose.prod.yml \
  -f infra/compose/docker-compose.prod.docker.yml \
  up -d --build --wait --wait-timeout 300 --pull never
```

The API applies database migrations before it starts serving.

**5. Check it is up**

```bash theme={null}
curl --fail https://app.example.com/health
```

Then open `https://app.example.com` and sign up with an address on your allowlist.

## What the overlay adds

The overlay adds a **supervisor** that starts and stops bot computers, the **computer** image build, and a one-time fix for data folder ownership. It points the API and worker at the supervisor and makes `docker` the computer provider.

The supervisor controls Docker on the host. It publishes no port and joins only the internal network. Each bot computer runs in its own container on its own network.

Without the overlay, the production file uses a remote computer provider (E2B by default). Set `SANDBOX_PROVIDER` and that provider's API key instead.

## Add a company

Each company gets its own organization with its own spaces, bots and usage. Create one with its owner:

```bash theme={null}
docker compose --env-file .env \
  -f infra/compose/docker-compose.prod.yml \
  -f infra/compose/docker-compose.prod.docker.yml \
  exec -e ORG_NAME="Example Co" -e OWNER_EMAIL=owner@example.com -e INVITED_BY=you@example.com \
  api node dist/cli/create-organization.js
```

If the owner already has an account, they become the owner straight away. Otherwise the command prints a one-use invite link, valid for seven days. See [Companies and people](https://docs.usecadence.gg/teams/companies-and-people.md).

## Upgrade

Pull the new code and rebuild:

```bash theme={null}
git pull
GIT_SHA=$(git rev-parse HEAD) docker compose --env-file .env \
  -f infra/compose/docker-compose.prod.yml \
  -f infra/compose/docker-compose.prod.docker.yml \
  up -d --build --wait --wait-timeout 300 --pull never
```

`up --wait` only reports success once the new API is healthy. When the computer image itself changes, rebuild it too:

```bash theme={null}
docker compose --env-file .env \
  -f infra/compose/docker-compose.prod.yml \
  -f infra/compose/docker-compose.prod.docker.yml \
  build computer
```

## Back up

The database, app data and HTTPS certificates live in named Docker volumes. The repository includes `infra/compose/backup-prod.sh`, which writes a verified database dump and app data archive. Copy backups off the server, encrypted. `docs/self-host.md` covers backup, restore and the optional in-app updater in detail.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.