feat: an option to setup remote server with docker compose build (#280)

* feat: remote setup with docker build option

* chore: update documentation

* chore: make script run in non tty

* chore: add warning about slow build

* chore: add more documentation

* feat: add FASTAPI_WORKERS parameter

* feat: add scaling docs

* feat: add update script

* fix: fix semver options in update_remote.sh
This commit is contained in:
Abhishek 2026-05-13 17:22:14 +05:30 committed by GitHub
parent b670004725
commit 59619e9eaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1086 additions and 145 deletions

View file

@ -6,7 +6,7 @@ description: "Deploy Dograh AI using Docker for local development and remote ser
Dograh AI can be deployed using Docker in two main configurations. Choose the option that best fits your needs:
- **Option 1**: For local development and testing on your own machine
- **Option 2**: For remote server deployment with HTTPS (using IP address)
- **Option 2**: For remote server deployment with HTTPS (using IP address). If you also have a custom domain, you can first deploy Dograh stack on your server using steps in this document and then proceed to the [Custom Domain](deployment/custom-domain) section.
## Option 1: Local Docker Deployment
@ -52,7 +52,7 @@ The Quick Start above relies on direct peer-to-peer WebRTC between your browser
- The call connects but no audio flows in either direction
- The browser console reports `iceConnectionState: failed`
- You are testing from a phone or another device on your LAN against the laptop running Docker
- You are testing from a browser on your smartphone or another device on your LAN against the laptop running Docker
- A VPN, corporate firewall, or strict NAT sits between the browser and Docker
For these cases, use the alternate local setup script which configures a coturn TURN server alongside the rest of the stack:
@ -90,17 +90,17 @@ Watch the video tutorial below for a step-by-step walkthrough of deploying Dogra
allowFullScreen
></iframe>
Deploy Dograh AI on a remote server to make it accessible from anywhere using your server's IP address. This setup includes HTTPS support via nginx reverse proxy with self-signed certificates. We need to serve the application over HTTPS, since modern browsers only allow microphone permissions for websites being served over HTTPS.
Deploy Dograh AI on a remote server to make it accessible from anywhere using your server's IP address. This setup includes HTTPS support via nginx reverse proxy with self-signed certificates. **We need to serve the application over HTTPS, since modern browsers only allow microphone permissions for websites being served over HTTPS**.
**We highly recommend you set up the platform on a fresh server, so that there are less chances of confliciting dependencies, and ports from other applications.**
<Warning>We highly recommend you set up the platform on a fresh server, so that there are less chances of confliciting dependencies, and ports from other applications.</Warning>
### Prerequisites
- A server with Docker and Docker Compose installed. It should have minimum of 8 GB RAM and 4 vCPUs.
- Public IP address for your server
- Public IP address for your server. You can also access the server using a local IP address in your VPC as long as its reachable from your browser.
- TCP Ports 80, 443, 3478, 5349 and UDP Ports 3478, 5349 and 49152:49200 reachable from Internet (Port 80 and 443 to access the UI and rest of the ports for WebRTC Signaling)
<Note>Please refer to your server hosting provider's documentaion on how you can open these ports in the firewall of the server.</Note>
<Note>IMPORTANT: Please double check the ports on your firewall. Please refer to your server hosting provider's documentaion on how you can open these ports in the firewall of the server.</Note>
### Quick Setup
@ -113,12 +113,15 @@ curl -o setup_remote.sh https://raw.githubusercontent.com/dograh-hq/dograh/main/
The script will prompt you for:
- Your server's public IP address
- A password for the TURN server (optional, press Enter for default)
- Deployment mode — press Enter for **prebuilt** (pulls official images, the recommended default) or pick **build** to compile images from source. Use **build** when you maintain a fork or want to deploy local customizations — see [Building from source](#building-from-source) below.
- Number of FastAPI workers (uvicorn processes nginx will load-balance) — press Enter for the default of `4`, or pick a value that suits your server's CPU count. See [Scaling](/deployment/scaling) for sizing guidance and how to change this on a running stack.
It will automatically:
- Download the docker-compose.yaml file
- Get the source — `docker-compose.yaml` only (prebuilt mode), or clone the full repo (build mode)
- Create and configure nginx.conf with your IP address
- Generate SSL certificates
- Create an environment file with TURN server configuration
- Write a `docker-compose.override.yaml` with build directives (build mode only)
### Start the Application
@ -126,12 +129,20 @@ It will automatically:
Please ensure that Docker Compose is installed on your machine before proceeding further. You can check whether its installed by running `docker compose version` command. If its not installed, please install it by following your server provider documentation.
</Note>
After the setup script completes, start Dograh:
After the setup script completes, start Dograh. The script prints the exact command to run at the end — it differs slightly between modes:
```bash
<CodeGroup>
```bash Prebuilt mode
cd dograh
sudo docker compose --profile remote up --pull always
```
```bash Build mode
cd dograh
sudo docker compose --profile remote up -d --build
```
</CodeGroup>
First boot in build mode takes several minutes — Docker has to build both the API and UI images before the stack comes up.
### Access Your Application
@ -162,12 +173,80 @@ The setup script creates the following files in the `dograh/` directory:
| File | Purpose |
|------|---------|
| `docker-compose.yaml` | Main Docker Compose configuration |
| `docker-compose.override.yaml` | Build directives for `api` and `ui` (**build mode only**) |
| `turnserver.conf` | Configuration for TURN server |
| `nginx.conf` | nginx reverse proxy configuration with your IP |
| `generate_certificate.sh` | Script to regenerate SSL certificates |
| `certs/local.crt` | Self-signed SSL certificate |
| `certs/local.key` | SSL private key |
| `.env` | Environment variables for TURN server |
| `.env` | Environment variables (TURN secret, JWT secret, FastAPI worker count) |
### Building from source
If you maintain a fork or want to ship local code changes without waiting for an official image release, pick **build** when the setup script prompts for deployment mode.
In this mode the script:
1. Clones the repo into `dograh/` (default: `dograh-hq/dograh@main`; the script will ask for an `owner/name` and `branch` so a fork can point at its own).
2. If you run the script from inside an existing dograh checkout, offers to use that checkout instead of cloning.
3. Generates a `docker-compose.override.yaml` next to `docker-compose.yaml` that swaps the `image:` directives for local `build:` directives. Docker Compose auto-loads the override file — you don't need to pass any `-f` flags.
#### Updating after a code change
Whenever the codebase changes — a `git pull`, a local edit, or a submodule bump — rebuild the `api` and `ui` images and recreate the running containers so they pick up the new code.
**TL;DR (rebuilds both services and restarts in one shot):**
```bash
cd dograh
sudo docker compose --profile remote up -d --build
```
`--build` rebuilds any service whose `build:` context has changed (both `api` and `ui` are wired through the `docker-compose.override.yaml` written by `setup_remote.sh`), and `up -d` recreates the affected containers so they run off the new image.
**Step-by-step** — use this when you're pulling fresh code from a remote, or want to be explicit:
```bash
cd dograh
# 1. Pull the latest source.
git pull
git submodule update --init --recursive # picks up pipecat submodule bumps
# 2. Rebuild both images.
sudo docker compose --profile remote build api ui
# 3. Recreate the running containers so they use the new images.
sudo docker compose --profile remote up -d
```
<Warning>If you update `pipecat` submodule, you must do `git submodule update --init --recursive`, or the Docker build step will not pick up `pipecat` changes</Warning>
**Rebuild a single service** — faster when you only changed one side:
```bash
sudo docker compose --profile remote build api # or: build ui
sudo docker compose --profile remote up -d api # or: up -d ui
```
**Force a clean rebuild** — needed when a base image changed, a `pip install` was added, or you suspect a stale layer:
```bash
sudo docker compose --profile remote build --no-cache api ui
sudo docker compose --profile remote up -d
```
**Verify the containers were actually recreated** — check the `CREATED` column shows a recent timestamp, not minutes/hours ago:
```bash
sudo docker compose --profile remote ps
```
To revert to pulling official images, delete `docker-compose.override.yaml` and start the stack with `--pull always` as in the [prebuilt flow](#start-the-application).
<Note>
Build mode is for **fork maintainers and self-hosters who want to deploy customized images** — not for active development on the code itself. For day-to-day contributor work (fast reload, IDE/LSP integration), use the [contributor setup](/contribution/setup) instead.
</Note>
### Next Steps
Now that you are able to create and test a voice agent, you can setup a custom domain and setup SSL using letsencrypt. Checkout [Custom Domain](custom-domain) for instructions on how to do that.

159
docs/deployment/scaling.mdx Normal file
View file

@ -0,0 +1,159 @@
---
title: "Scaling"
description: "Run multiple FastAPI worker processes behind nginx for higher throughput"
---
By default, the Dograh API container runs a single uvicorn worker. For production traffic — especially with many concurrent voice calls (long-lived WebSockets) — you'll want multiple workers. Dograh ships with built-in support for this: nginx load-balances across N independent uvicorn processes using a `least_conn` strategy.
This page covers how the multi-worker setup works, how to choose a worker count at install time, and how to change it on a running stack.
<Warning>
Multi-worker support requires **Dograh v1.29.0 or newer**. Earlier releases used `uvicorn --workers` and ship a different `setup_remote.sh` / `start_services_docker.sh` / `nginx.conf` layout — the steps below will not work on them. If your stack is older, [update first](/deployment/update) and then come back to this page.
</Warning>
## How it works
The API container starts `FASTAPI_WORKERS` separate uvicorn processes, each bound to its own port (`8000`, `8001`, `8002`, …). nginx exposes a single upstream `dograh_api` that includes all worker ports and routes new requests to whichever worker currently has the **fewest active connections**.
```
┌───────────────────────────────────┐
│ api container │
│ uvicorn worker 0 → :8000 │
browser ──► nginx ──► │ uvicorn worker 1 → :8001 │
(443) (least_conn) uvicorn worker 2 → :8002 │
│ uvicorn worker 3 → :8003 │
└───────────────────────────────────┘
```
<Note>
This is intentionally **not** `uvicorn --workers N` (the built-in pre-fork mode). With pre-fork, the Linux kernel distributes new TCP connections across workers via `accept()` — fine for short HTTP requests, but long-lived WebSockets stick to whichever worker first accepted them. A handful of unlucky workers end up handling most of the streaming traffic while the others idle. Routing at the nginx layer with `least_conn` knows the actual per-worker connection count and distributes WebSockets evenly.
</Note>
The `ari_manager` and `campaign_orchestrator` processes inside the API container stay as **singletons** regardless of `FASTAPI_WORKERS` — they coordinate global state (Asterisk channels, campaign scheduling) and should not be duplicated. ARQ background workers are controlled separately via `ARQ_WORKERS`.
## Choosing a worker count
A safe starting point is **one worker per available vCPU**, capped at 8 unless you've profiled your workload. The [Remote Server Deployment prerequisites](/deployment/docker#prerequisites) ask for a minimum of 4 vCPUs, so:
| vCPUs | Suggested `FASTAPI_WORKERS` |
|-------|-----------------------------|
| 4 | 4 |
| 8 | 68 |
| 16+ | profile first |
Each worker holds its own Python process and memory — budget roughly **300500 MB RAM per worker** in addition to the postgres/redis/minio overhead. If you're near the 8 GB RAM minimum and see OOMs, drop the worker count before adding more.
## Setting the worker count at install time
`setup_remote.sh` prompts for the worker count alongside the other configuration:
```
Number of FastAPI workers (uvicorn processes nginx will load-balance):
[4]:
```
Press Enter for the default (`4`) or enter a different positive integer. Non-interactive callers (cloud-init, CI, Terraform) can set the value via environment variable instead:
```bash
SERVER_IP=... TURN_SECRET=... FASTAPI_WORKERS=8 ./setup_remote.sh
```
The script wires the value into two places:
- **`.env`** — sets `FASTAPI_WORKERS=N`, which `docker-compose.yaml` substitutes into the API container's environment.
- **`nginx.conf`** — generates an `upstream dograh_api` block with one `server api:800X` entry per worker.
Both must agree, which is why the script generates them together.
## Changing the worker count on a running stack
Once Dograh is running, increasing or decreasing the worker count is a two-file edit plus a restart. You'll touch:
1. **`.env`** — controls how many uvicorn processes the API container spawns.
2. **`nginx.conf`** — controls which worker ports nginx forwards to.
<Warning>
Both files must stay in sync. If `.env` says `FASTAPI_WORKERS=8` but `nginx.conf` only lists 4 upstream servers, half your workers will be idle. If `nginx.conf` lists more upstreams than there are workers, those upstreams will throw connection errors and trip the `proxy_next_upstream` fallback.
</Warning>
### Steps
All commands run from your `dograh/` directory (the one with `docker-compose.yaml`).
**1. Edit `.env`** and change the `FASTAPI_WORKERS` line:
```bash
# Before
FASTAPI_WORKERS=4
# After
FASTAPI_WORKERS=8
```
**2. Edit `nginx.conf`** and update the `upstream dograh_api` block so it has exactly one `server api:800X` line per worker, with ports starting at `8000`:
```nginx
upstream dograh_api {
least_conn;
server api:8000 max_fails=3 fail_timeout=10s;
server api:8001 max_fails=3 fail_timeout=10s;
server api:8002 max_fails=3 fail_timeout=10s;
server api:8003 max_fails=3 fail_timeout=10s;
server api:8004 max_fails=3 fail_timeout=10s; # ← new
server api:8005 max_fails=3 fail_timeout=10s; # ← new
server api:8006 max_fails=3 fail_timeout=10s; # ← new
server api:8007 max_fails=3 fail_timeout=10s; # ← new
keepalive 32;
}
```
To **scale down**, remove the trailing `server` lines so the list matches the new `FASTAPI_WORKERS` value.
**3. Recreate the affected containers.** The simplest path — brief downtime, no surprises:
```bash
sudo docker compose --profile remote down
sudo docker compose --profile remote up -d
```
If you want to avoid downtime and your stack is healthy, you can recreate only the `api` and `nginx` containers:
```bash
sudo docker compose --profile remote up -d --force-recreate api nginx
```
`--force-recreate` ensures the api container picks up the new `FASTAPI_WORKERS` value and nginx re-reads the updated `nginx.conf` (which is mounted read-only from disk).
**4. Verify.** Confirm the right number of uvicorn processes are running. The API image is slim and doesn't include `ps`, so use Docker's host-side view instead:
```bash
sudo docker compose --profile remote top api | grep uvicorn
```
You should see one line per worker. To confirm the bound ports, check the startup logs — each worker logs an `Uvicorn running on http://0.0.0.0:800X` line on boot:
```bash
sudo docker compose --profile remote logs api | grep "Uvicorn running"
```
Then hit the API through nginx to confirm requests still flow:
```bash
curl -k https://YOUR_SERVER_IP/api/v1/health
```
### Why not just re-run `setup_remote.sh`?
`setup_remote.sh` refuses to overwrite an existing install by design — re-running it would regenerate `OSS_JWT_SECRET` (logging everyone out), reset the TURN shared secret (breaking WebRTC auth on connected clients), and regenerate SSL certificates. The two-file edit above is the supported way to change worker count after install.
If you genuinely want a clean reinstall, see the `DOGRAH_FORCE_OVERWRITE=1` escape hatch documented in the script.
## What this does not scale
Multi-worker mode scales the HTTP/WebSocket API surface. It does **not** scale:
- **ARQ background workers** — controlled by `ARQ_WORKERS` (defaults to 1). Increase this in the API container's environment if your background job queue backs up.
- **`ari_manager` / `campaign_orchestrator`** — singletons by design; they don't benefit from extra processes.
- **Postgres, Redis, MinIO** — each runs as a single container in the stack. For production-scale Postgres you'd run a managed service and point `DATABASE_URL` at it; the same applies to Redis and S3-compatible storage.
For multi-machine horizontal scaling (separate API containers across hosts), see the [Custom Domain](/deployment/custom-domain) guide for the load-balancer-in-front-of-multiple-hosts pattern — it's the same idea as the in-container `least_conn` upstream, just one layer higher.

View file

@ -5,6 +5,12 @@ description: "Update your self-hosted Dograh stack to a newer image version"
This guide covers updating a Dograh stack you've already deployed with [Docker](/deployment/docker) or a [custom domain](/deployment/custom-domain). You run commands from the same directory that contains your `docker-compose.yaml` (this is the `dograh/` directory if you used `setup_remote.sh`).
There are three update flows depending on how you deployed:
- **Remote, prebuilt mode** (most users) — use [`update_remote.sh`](#remote-prebuilt-mode-recommended) below.
- **Local Docker** — pull images and restart; see [Local deployment](#local-deployment).
- **Remote, build mode** (you have a `docker-compose.override.yaml`) — update via git; see [Updating a source build](#updating-a-source-build).
## Find an image version
Dograh publishes two images — `dograh-api` and `dograh-ui` — to both container registries:
@ -12,81 +18,64 @@ Dograh publishes two images — `dograh-api` and `dograh-ui` — to both contain
- **GitHub Container Registry** — [github.com/orgs/dograh-hq/packages](https://github.com/orgs/dograh-hq/packages)
- **Docker Hub** — [hub.docker.com/u/dograhai](https://hub.docker.com/u/dograhai)
Each release is published under two kinds of tags:
Each release is published under two kinds of tags. Note the formats differ between GitHub releases and the Docker image tags — `update_remote.sh` understands both and normalizes for you.
| Tag style | Example | When to use |
|-----------|---------|-------------|
| **Release tag** | `v0.8.2` | Stable, recommended for production |
| **Git commit SHA** | `a1b2c3d` | Bleeding edge — any commit merged to `main` |
| `latest` | `latest` | Tracks the most recent release tag |
| Where | Tag format | Example | When to use |
|-------|-----------|---------|-------------|
| GitHub release tag | `dograh-vX.Y.Z` | `dograh-v1.28.0` | What you see at [github.com/dograh-hq/dograh/releases](https://github.com/dograh-hq/dograh/releases) |
| Docker image tag (semver) | `X.Y.Z` | `1.28.0` | Stable, recommended for production |
| Docker image tag (SHA) | short SHA | `a1b2c3d` | Bleeding edge — any commit merged to `main` |
| Docker image tag (`latest`) | `latest` | `latest` | Tracks the most recent release tag |
<Warning>
Always update **`dograh-api`** and **`dograh-ui`** to the **same tag**. The two images are built from the same commit and the UI expects API responses in a matching shape — mixing versions will break the app.
Always update **`dograh-api`** and **`dograh-ui`** to the **same tag**. The two images are built from the same commit and the UI expects API responses in a matching shape — mixing versions will break the app. `update_remote.sh` handles this for you automatically.
</Warning>
## Option A: Update to the latest release
## Remote, prebuilt mode (recommended)
If your `docker-compose.yaml` uses `:latest` (the default), just pull and restart:
`update_remote.sh` is the supported path for updating a stack created with `setup_remote.sh`. In one shot it:
<CodeGroup>
```bash Local deployment
docker compose down
docker compose up --pull always
```
```bash Remote deployment
cd dograh
sudo docker compose --profile remote down
sudo docker compose --profile remote up --pull always
```
</CodeGroup>
- Asks for a target version (defaults to the latest release tag on GitHub).
- Pulls `docker-compose.yaml` at that version and pins both `api` and `ui` images to it.
- Regenerates `nginx.conf` and `turnserver.conf` from the upstream templates, so newer features (like [multi-worker scaling](/deployment/scaling)) are wired up correctly without manual editing.
- Reads your existing `.env` and appends any new required keys with safe defaults — your `OSS_JWT_SECRET`, `TURN_SECRET`, and other values are never touched.
- Backs up every file it changes with a `.bak.<timestamp>` suffix.
`--pull always` forces Docker to fetch the latest `:latest` from the registry instead of reusing your cached image.
## Option B: Pin a specific tag
To update (or roll back) to a specific release or commit, edit `docker-compose.yaml` and change the `image:` lines for both `api` and `ui` services to the same tag.
Open the file:
From your install directory:
```bash
nano docker-compose.yaml
cd dograh
curl -o update_remote.sh https://raw.githubusercontent.com/dograh-hq/dograh/main/scripts/update_remote.sh
bash update_remote.sh
```
Find these two lines:
You'll be prompted for the target version, defaulting to the most recent release. Accepted forms: bare semver (`1.28.0`), v-prefixed (`v1.28.0`), the full GitHub tag (`dograh-v1.28.0`), or `main` for bleeding edge — the script normalizes them. Non-interactive callers can set it via environment variable and skip the confirmation prompt:
```yaml
api:
image: ${REGISTRY:-dograhai}/dograh-api:latest
ui:
image: ${REGISTRY:-dograhai}/dograh-ui:latest
```bash
TARGET_VERSION=1.28.0 DOGRAH_UPDATE_YES=1 bash update_remote.sh
```
Replace `:latest` with your chosen tag on **both** services — for example:
After the script finishes, apply the update by recreating the stack:
```yaml
api:
image: ${REGISTRY:-dograhai}/dograh-api:v0.8.2
ui:
image: ${REGISTRY:-dograhai}/dograh-ui:v0.8.2
```bash
sudo docker compose --profile remote down
sudo docker compose --profile remote up -d --pull always
```
<Note>
You can use either registry. Leave `REGISTRY` unset for Docker Hub (`dograhai`), or export `REGISTRY=ghcr.io/dograh-hq` to pull from GitHub Container Registry.
The script overwrites `docker-compose.yaml`, `nginx.conf`, and `turnserver.conf` from upstream templates. If you've made local edits to any of these (extra environment variables, custom ports, modified nginx routes), check the `.bak.<timestamp>` files after the update and re-apply your edits.
</Note>
Then bring the stack down and back up:
## Local deployment
<CodeGroup>
```bash Local deployment
For local Docker installs (the [Quick Start](/deployment/docker#quick-start) flow or `setup_local.sh`), there are no host-side config files to refresh — pull new images and restart:
```bash
docker compose down
docker compose up --pull always
```
```bash Remote deployment
cd dograh
sudo docker compose --profile remote down
sudo docker compose --profile remote up --pull always
```
</CodeGroup>
To pin a specific version instead of `latest`, edit `docker-compose.yaml` and change both `image:` lines for `api` and `ui` to the same tag (e.g. `:1.28.0` — Docker image tags use bare semver, no `v` prefix), then run the commands above.
## Verify the update
@ -101,13 +90,54 @@ You should see the API and UI both running the tag you pinned.
Hit the health endpoint to confirm the API is responding:
```bash
curl http://localhost:8000/api/v1/health
curl -k https://YOUR_SERVER_IP/api/v1/health # remote
curl http://localhost:8000/api/v1/health # local
```
## Roll back
If something breaks, roll back by pinning the previous tag using the same process in **Option B** and restarting. Your Postgres data volume persists across `down`/`up` cycles, so agents and call history are preserved.
`update_remote.sh` saves backups of every file it touched. To roll back, restore them and recreate the stack — the exact commands (including the timestamp it used) are printed at the end of the script's output. The generic form:
```bash
cd dograh
for f in docker-compose.yaml nginx.conf turnserver.conf .env; do
[[ -f "$f.bak.<timestamp>" ]] && cp "$f.bak.<timestamp>" "$f"
done
sudo docker compose --profile remote down
sudo docker compose --profile remote up -d
```
Your Postgres data volume persists across `down`/`up` cycles, so agents and call history are preserved.
<Warning>
Rolling back across a database migration is not always safe — if the newer release ran a schema migration, downgrading may leave the DB in a state the older API doesn't understand. If in doubt, [open an issue](https://github.com/dograh-hq/dograh/issues) before rolling back.
</Warning>
## Updating a source build
If you deployed in **build mode** (you'll have a `docker-compose.override.yaml` in your install directory), `update_remote.sh` deliberately refuses to run — you already have the full repo locally and update via git:
```bash
cd dograh
git fetch
# Track latest main:
git pull
# Or pin to a specific release (git tag format is dograh-vX.Y.Z):
git checkout dograh-v1.28.0
# Pick up pipecat and other submodule bumps
git submodule update --init --recursive
# Rebuild and restart
sudo docker compose --profile remote build
sudo docker compose --profile remote up -d
```
<Warning>
If you update the `pipecat` submodule, you **must** run `git submodule update --init --recursive` before rebuilding, or the Docker build will not pick up `pipecat` changes.
</Warning>
If you maintain a fork with local customizations on top of upstream, merging conflicts in `docker-compose.yaml`, `nginx.conf`, `turnserver.conf`, or `setup_remote.sh` is up to you — resolve them as you would any other git merge. Leave `OSS_JWT_SECRET` and `TURN_SECRET` in `.env` unchanged across updates to preserve sessions and WebRTC auth.
The same migration warning above applies: rolling back across a schema change can leave the DB in a state the older API can't read.

View file

@ -145,6 +145,7 @@
"deployment/introduction",
"deployment/docker",
"deployment/custom-domain",
"deployment/scaling",
"deployment/update",
"deployment/heroku"
]

View file

@ -1,11 +1,10 @@
---
title: "Introduction"
description: "Open-source alternative to Vapi - build voice AI agents with full control & transparency"
---
## About Dograh
**Dograh is an open-source alternative to Vapi** - Dograh helps you build voice AI agents with an easy drag-and-drop workflow builder. Unlike proprietary solutions like Vapi, Dograh gives you:
**Dograh is an open-source alternative to platforms like Vapi and Retell** - Dograh helps you build voice AI agents with an easy drag-and-drop workflow builder. Unlike proprietary solutions like Vapi, Dograh gives you:
- **100% open source** - no vendor lock-in, full transparency
- **Self-hostable** - deploy anywhere, own your infrastructure
- **Complete control** - every line of code is open and customizable
@ -25,7 +24,7 @@ Watch the following video to learn about Dograhs capabilities.
## Setting up
Get the platform up and running using Docker with a single command on your local computer. If you are looking to deploy the platform on a server, please check the [Deployment](deployment/introduction) section.
Get the platform up and running using Docker with a single command on your local computer. If you are looking to deploy the platform on a server different than your local machine, please check the [Deployment](deployment/introduction) section.
<Note>We collect anonymous usage data to improve the product. You can opt out by setting the `ENABLE_TELEMETRY` to `false` in the below command.</Note>

View file

@ -1,19 +1,16 @@
---
title: "Agent Stream"
description: "Stream audio to a Dograh agent over a WebSocket using inline provider credentials"
description: "Stream audio to a Dograh agent over a WebSocket"
---
## Overview
Agent Stream is a WebSocket endpoint that lets an external caller drive a Dograh agent run by passing everything inline in the query string — including the telephony provider's credentials. Unlike the standard inbound webhook flow, this path does **not** require a stored telephony configuration in your Dograh organization. You bring the credentials with you when you connect.
Agent Stream is a WebSocket endpoint that lets a telephony provider point its media stream at a single URL and drive a Dograh agent run. The agent UUID in the URL selects the agent; provider-specific identifiers in the query string (for Cloudonix: `Domain`) tell Dograh which stored telephony configuration to use for that call. The bearer token and other credentials are never passed in the URL — they live in the stored configuration and are used by Dograh to validate the session and to issue provider API calls (hangup, transfer) during the call.
This is useful when:
- You manage your telephony tenant outside Dograh and only want Dograh as the agent runtime
- You're integrating Dograh into a SIP gateway or in-house dialer
- You need a single endpoint that doesn't need per-tenant DB rows for every caller
The endpoint authenticates with a Dograh **API key** (so the connection itself is authorized against your organization), and routes the audio through the named provider's serializer.
- You're integrating Dograh into a SIP gateway or in-house dialer that already speaks a supported provider's streaming protocol
- You want one stable endpoint per agent rather than wiring up an inbound webhook per phone number
<Warning>
Agent Stream currently supports the **Cloudonix** provider only. Other providers
@ -26,16 +23,15 @@ The endpoint authenticates with a Dograh **API key** (so the connection itself i
## Endpoint
```
wss://app.dograh.com/api/v1/agent-stream/{workflow_uuid}
wss://app.dograh.com/api/v1/agent-stream/{agent_uuid}
```
`{workflow_uuid}` is the agent's stable UUID (see [Get the Agent UUID](#get-the-agent-uuid) below). On self-hosted deployments, replace `app.dograh.com` with your backend host.
`{agent_uuid}` is the agent's stable UUID (see [Get the Agent UUID](#get-the-agent-uuid) below). On self-hosted deployments, replace `app.dograh.com` with your backend host.
## Prerequisites
- A Dograh agent (workflow) — published or in draft is fine
- A Dograh API key with access to the organization that owns the agent
- Provider credentials (for Cloudonix: bearer token + domain ID)
- A Cloudonix telephony configuration in your Dograh organization whose `domain_id` matches the `Domain` you pass on the URL. Dograh uses the bearer token from this configuration to validate the call session and to issue provider API calls (hangup, transfer).
## Get the Agent UUID
@ -53,47 +49,25 @@ The Agent UUID is the workflow's stable identifier — it doesn't change when ve
2. Scroll to the **Agent UUID** section (also linked in the right-side nav)
3. Click the UUID code block, or use the **Copy UUID** button
## Create a Dograh API key
Agent Stream auth uses your Dograh API key — not your provider's bearer token. The provider credentials go in separate query params (see [URL parameters](#url-parameters) below).
1. From the dashboard, navigate to **API Keys**
2. Click **Create API key**, give it a name, and copy the generated key (it starts with `dg_`)
3. Store it securely — Dograh shows the full key only once at creation time
For programmatic management, see the [API Keys reference](/api-reference/api-keys).
<Note>
API keys are scoped to an organization. The agent referenced by the URL must
belong to the same organization as the key, otherwise the connection is
rejected with WebSocket close code `1008`.
</Note>
## Connect to the WebSocket
### URL parameters
| Param | Required | Description |
| --- | --- | --- |
| `api_key` | Yes | Your Dograh API key (`dg_...`). Authorizes the connection. |
| `provider` | Yes | Provider name. Currently only `cloudonix` is supported. |
| `session` | Yes (cloudonix) | Cloudonix domain bearer token used to drive the call (hangup, etc.). |
| `AccountSid` | Yes (cloudonix) | Cloudonix domain ID. |
| `CallSid` | Yes (cloudonix) | Cloudonix call SID for this session. |
| `callId` | No | SIP-side call identifier; persisted on the workflow run for record-keeping. |
| `Domain` | Yes (cloudonix) | Cloudonix domain ID. Dograh uses this to look up the matching stored telephony configuration and retrieve the bearer token used for provider API calls. |
| `callId` / `CallSid` | No | Call identifier from your side; persisted on the workflow run's `gathered_context` as `call_id`. The Cloudonix call SID used for streaming is taken from the `start` event payload, not this param. |
| `from` | No | Caller phone number, persisted on the workflow run as `caller_number`. |
| `to` | No | Called phone number, persisted on the workflow run as `called_number`. |
### Cloudonix example
```
wss://app.dograh.com/api/v1/agent-stream/{workflow_uuid}
?api_key=dg_xxxxxxxxxxxxxxxx
&provider=cloudonix
&session={CLOUDONIX_BEARER_TOKEN}
&AccountSid={CLOUDONIX_DOMAIN_ID}
&CallSid={CALL_SID}
&callId={SIP_CALL_ID}
wss://app.dograh.com/api/v1/agent-stream/{agent_uuid}
?provider=cloudonix
&Domain={CLOUDONIX_DOMAIN_ID}
&callId={CALL_ID}
&from=+15555550100
&to=+15555550199
```
@ -104,23 +78,22 @@ Use this URL inside the CXML `<Stream>` your Cloudonix Voice Application returns
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Connect>
<Stream url="wss://app.dograh.com/api/v1/agent-stream/{workflow_uuid}?api_key=dg_...&provider=cloudonix&session=...&AccountSid=...&CallSid=...&callId=...&from=...&to=..."/>
<Stream url="wss://app.dograh.com/api/v1/agent-stream/{agent_uuid}?provider=cloudonix&Domain=...&callId=...&from=...&to=..."/>
</Connect>
<Pause length="40"/>
</Response>
```
The first two messages on the socket should be Cloudonix's standard `connected` and `start` events (Twilio-compatible framing). Dograh extracts `streamSid` and `callSid` from the `start` event and begins streaming audio.
The first two messages on the socket should be Cloudonix's standard `connected` and `start` events (Twilio-compatible framing). Dograh extracts `streamSid` and `callSid` from the `start` event payload, validates the session against Cloudonix using the bearer token from the stored telephony configuration matched by `Domain`, and then begins streaming audio.
## Workflow run lifecycle
When the WebSocket is accepted, Dograh:
1. Resolves your API key to a user and organization
2. Looks up the workflow by `workflow_uuid`, scoped to that organization
3. Runs a quota check
4. Creates a new `WorkflowRun` (`call_type=inbound`, `mode=cloudonix`, name `WR-AGS-XXXXXXXX`) with the `from`/`to`/`callId`/`AccountSid` fields stamped on `initial_context`
5. Transitions the run to `running` and starts the agent pipeline
1. Looks up the workflow by `agent_uuid`
2. Runs a quota check against the workflow's owning user
3. Creates a new `WorkflowRun` (`call_type=inbound`, `mode=cloudonix`, name `WR-AGS-XXXXXXXX`) with the `from`/`to` numbers stamped on `initial_context`, the `callId`/`CallSid` stored as `call_id` on `gathered_context`, and `Domain` recorded under the run's `inbound_webhook` log
4. Transitions the run to `running` and starts the agent pipeline
The run is visible under the agent's **Runs** tab as soon as it's minted, just like an inbound or outbound call.
@ -128,12 +101,11 @@ The run is visible under the agent's **Runs** tab as soon as it's minted, just l
| Code | Reason |
| --- | --- |
| `1008` | Auth or routing failure — invalid API key, unknown provider, workflow not found in your organization, or quota exceeded |
| `1008` | Routing failure — unknown provider, workflow not found, or quota exceeded |
| `1011` | Server-side failure or unsupported provider for Agent Stream |
| `4400` | Provider-level handshake error — for cloudonix, missing `session`/`AccountSid` or malformed `connected`/`start` events |
| `4400` | Provider-level handshake error — for cloudonix, missing `Domain`, no matching telephony configuration, missing bearer token on the configuration, malformed `connected`/`start` events, or session validation failed against Cloudonix |
## Security notes
- Treat the URL as a secret — both the Dograh API key and the provider bearer token sit in the query string. Store and transmit it only over TLS, and avoid logging the raw URL in places where access is broader than your operations team.
- Rotate the Dograh API key from the dashboard if you suspect it has leaked.
- The Dograh API key authorizes the connection against your organization. The provider bearer token is only used by Dograh to drive provider-side actions (e.g. hangup) — it is not stored on the workflow run.
- Treat the URL as a secret — the agent UUID itself authorizes the connection. Store and transmit it only over TLS, and avoid logging the raw URL in places where access is broader than your operations team.
- No bearer tokens or provider secrets are passed in the URL. Provider credentials live in the stored telephony configuration (matched by `Domain` for Cloudonix) and are used server-side by Dograh to validate the session and issue provider API calls.