DockerSelf-HostingAI

Dograh with Docker: Self-Hosted Voice AI Agents (Linux, Windows, Mac)

Before you start

  • 21 min read

Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#

Dograh is an open-source, self-hostable platform for building voice AI agents, the kind of product Vapi and Retell sell as a service: you design the conversation as a graph, talk to it in your browser, and later connect a phone number.

Dograh official logo

Logo: the official Dograh project (github.com/dograh-hq/dograh)

A call runs three jobs in a loop: speech to text (STT), a language model (LLM), and text to speech (TTS). Dograh runs the loop and the builder; you pick who does each job. It suits appointment booking, lead screening or a support line.

Read this first: the default "Dograh" mode uses Dograh's hosted service for all three jobs, so audio and text leave your machine. Fully local needs your own models (Step 6).

WhatValue (official sources, read October 2026)
Current version1.49.0, released 12 October 2026 (GitHub latest release)
Images to pindograhai/dograh-api:1.49.0 and dograhai/dograh-ui:1.49.0 (tags checked on Docker Hub)
LicenceBSD 2-Clause (LICENSE in the repository)
Time neededAbout 20 minutes plus model downloads (our estimate; the docs say the first start takes 2-3 minutes)
SizeAbout 540 MB and 100 MB per image, compressed (Docker Hub). No RAM measured by us
Ports3010 (UI), 8000 (API), 5432 (Postgres), 6379 (Redis), 9000 and 9001 (MinIO)
Official docsdocs.dograh.com and the repository

Screenshot of the official Dograh Create Voice Agent page with Call Type, Use Case and Activity Description fields

Image: the official Dograh docs (Your First Agent in 5 Minutes)

At a glance:

  • What it is: a visual agent builder, a call engine with a browser test mode, and run history with transcripts and recordings.
  • Stack: five containers: UI, API, Postgres, Redis and MinIO.
  • Cloud or local: each of STT, LLM and TTS can be a cloud provider or a local OpenAI-compatible server such as Ollama or Speaches.
  • Phone calls: Dograh sells no numbers or minutes. You bring Twilio, Telnyx, Vonage and similar, or your own PBX.
  • Telemetry: anonymous usage data is on by default; you can switch it off.

How it fits together#

You build and test in the UI. The API runs each call: caller audio goes to STT, the text to the LLM, the reply to TTS.

Flow of a Dograh call: you open the Dograh UI on port 3010 in a browser, the UI calls the Dograh API on port 8000 which saves agents and recordings to Postgres, Redis and MinIO and sends caller audio to speech to text, the transcript to a language model and the reply text to text to speech; an optional phone provider reaches the API over an HTTPS webhook

Drawn for this guide from the official compose file and docs. Dashed boxes are only for real phone calls.

Before you start#

You need Docker, 10 GB of disk and 8 GB of RAM, of which 4 GB must be available to Docker. These are the docs' minimums for the platform alone; local models need more.

SystemRequirements (prerequisites page)
LinuxKernel 3.10+, x86_64 or ARM64, Docker 20.10+ with Compose, curl, 2 CPU cores
WindowsWindows 10 or 11 with WSL2 and Docker Desktop (4 GB RAM, 8 recommended; 2 CPUs, 4 recommended)
MacmacOS 10.15+, Docker Desktop, curl. The images are also arm64

Our override file in Step 3 needs Docker Compose 2.24.4 or newer:

Run in terminalbash
docker --version
docker compose version

Checklist:

  • [ ] Docker running, Compose 2.24.4 or newer
  • [ ] Ports 3010 and 8000 free (lsof -i :3010 on Linux and Mac, netstat -ano | findstr :3010 in PowerShell)
  • [ ] A place to keep backups of .env and the Dograh folder
  • [ ] For HTTPS: a domain with a DNS record for the server, and ports 80 and 443 free
  • [ ] For phone calls: an account with a telephony provider

Tested by DIY Lab Hub: not yet. Linux = not run, Windows = not run, Mac = not run. The commands come from the official compose file, scripts and docs at release 1.49.0, read in October 2026.

Step 1: Create the folder#

Compose file, .env, backups and model files live in one folder. Postgres, Redis and MinIO keep the named Docker volumes of the official file.

Linux#

Run in terminalbash
mkdir -p ~/docker_volumes/dograh/{backups,models}
cd ~/docker_volumes/dograh

Windows (PowerShell)#

Codepowershell
New-Item -ItemType Directory -Force "$HOME\docker_volumes\dograh\backups","$HOME\docker_volumes\dograh\models" | Out-Null
Set-Location "$HOME\docker_volumes\dograh"

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
mkdir -p ~/docker_volumes/dograh/{backups,models}
cd ~/docker_volumes/dograh

Check it worked. docker compose version shows 2.24.4 or higher and the folder exists.

Tip: The folder name becomes the Compose project name (volumes like dograh_postgres_data). Keep the name dograh.

Step 2: Download the official files and create the secrets#

The compose file will not start alone: it needs a .env with secrets. The official script creates them, then asks Start Dograh now? [Y/n]. Answer n. Yes starts the stack with the tunnel profile, which opens a public *.trycloudflare.com address to your API. We do not use that; Step 3 starts Dograh without it.

We download from the 1.49.0 release tag. The files at the tag and at main were identical when we checked.

Linux#

Run in terminalbash
curl -o docker-compose.yaml https://raw.githubusercontent.com/dograh-hq/dograh/dograh-v1.49.0/docker-compose.yaml
curl -o start_docker.sh https://raw.githubusercontent.com/dograh-hq/dograh/dograh-v1.49.0/scripts/start_docker.sh
chmod +x start_docker.sh
./start_docker.sh

Windows (PowerShell)#

Codepowershell
Invoke-WebRequest -OutFile docker-compose.yaml https://raw.githubusercontent.com/dograh-hq/dograh/dograh-v1.49.0/docker-compose.yaml
Invoke-WebRequest -OutFile start_docker.ps1 https://raw.githubusercontent.com/dograh-hq/dograh/dograh-v1.49.0/scripts/start_docker.ps1
.\start_docker.ps1

If PowerShell refuses to run the script, run Unblock-File .\start_docker.ps1 first (our note, not from the docs).

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
curl -o docker-compose.yaml https://raw.githubusercontent.com/dograh-hq/dograh/dograh-v1.49.0/docker-compose.yaml
curl -o start_docker.sh https://raw.githubusercontent.com/dograh-hq/dograh/dograh-v1.49.0/scripts/start_docker.sh
chmod +x start_docker.sh
./start_docker.sh

Check it worked. The script prints lines such as "Created OSS_JWT_SECRET in .env." (from its source; we did not run it) and ends with "Dograh was not started." after you type n.

Tip: .env now holds every password; back it up. The Postgres password is baked into its volume at first start, so losing .env hurts.

Step 3: Pin the version, close extra ports and start#

The official file uses :latest and publishes Postgres (5432) and Redis (6379) on every interface. Our override file, which Compose reads automatically, does four things:

  • pins dograh-api and dograh-ui to 1.49.0 (the docs say both must use the same tag);
  • publishes the API and UI on 127.0.0.1 only;
  • removes the Postgres and Redis host ports (other containers still reach them);
  • lets the API container reach the host as host.docker.internal on Linux too.

We also switch telemetry off in .env.

Linux#

Run in terminalbash
cat > docker-compose.override.yaml << 'EOF'
services:
  api:
    image: ${REGISTRY:-dograhai}/dograh-api:1.49.0
    ports: !override
      - "127.0.0.1:8000:8000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
  ui:
    image: ${REGISTRY:-dograhai}/dograh-ui:1.49.0
    ports: !override
      - "127.0.0.1:3010:3010"
  postgres:
    ports: !reset []
  redis:
    ports: !reset []
EOF
echo "ENABLE_TELEMETRY=false" >> .env
chmod 600 .env
docker compose up -d
docker compose ps

Windows (PowerShell)#

Codepowershell
$override = @'
services:
  api:
    image: ${REGISTRY:-dograhai}/dograh-api:1.49.0
    ports: !override
      - "127.0.0.1:8000:8000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
  ui:
    image: ${REGISTRY:-dograhai}/dograh-ui:1.49.0
    ports: !override
      - "127.0.0.1:3010:3010"
  postgres:
    ports: !reset []
  redis:
    ports: !reset []
'@
[System.IO.File]::WriteAllText("$PWD\docker-compose.override.yaml", $override)
Add-Content -Path .env -Value "ENABLE_TELEMETRY=false"
docker compose up -d
docker compose ps

Mac (Terminal)#

Same commands as Linux. Docker Desktop already knows host.docker.internal; the extra line does no harm.

Check it worked. The first start downloads the images (2-3 minutes per the docs). docker compose ps lists api, ui, postgres, redis and minio; the API turns healthy after its 60-second start period. The health endpoint answers with JSON that includes auth_provider (local by default, per the docs):

Run in terminalbash
curl http://localhost:8000/api/v1/health

Windows: Invoke-RestMethod http://localhost:8000/api/v1/health.

Tip: After any .env change run docker compose up -d, not docker compose restart. The README warns that restart does not apply .env changes.

Step 4: Open the UI and create your account#

Linux#

Open http://localhost:3010 and sign up with an email and password (built-in local login).

Windows (PowerShell)#

Open http://localhost:3010 in Edge or Chrome and sign up the same way.

Mac (Terminal)#

Open http://localhost:3010 in Safari or Chrome and sign up.

Anyone who can reach the page can sign up, so close signup once your account exists:

Run in terminalbash
echo "ENABLE_SIGNUP=false" >> .env
docker compose up -d

PowerShell: Add-Content -Path .env -Value "ENABLE_SIGNUP=false", then docker compose up -d.

Check it worked. You see the agent list, and the login page no longer shows a sign-up link (the docs describe this for ENABLE_SIGNUP=false).

Tip: http://localhost:3010 allows the microphone without HTTPS, because browsers treat localhost as secure. A LAN address does not, so the microphone is blocked there.

Step 5: Build your first agent and test it#

Linux#

  1. Create an agent. Pick Inbound or Outbound (it only changes the starting prompt) and describe the job, for example "Book a haircut appointment, ask for the preferred date and time, confirm before ending the call."
  2. In the editor, start a Web Call (Test Audio), allow the microphone and talk.
  3. Use Test Chat to iterate on prompts as text.

Windows (PowerShell)#

Same steps in Edge or Chrome. With silence on the call, see "Common mistakes".

Mac (Terminal)#

Same steps. Allow the microphone for your browser if macOS asks.

Check it worked. The live transcript shows your words and the agent's reply. The default Dograh mode needs no key: the hosted service does the speech work.

Step 6: Choose your models: cloud or fully local#

Open Models (and Providers for accounts) in the sidebar. Version 1.49 has three modes:

ModeWhat happensWhere the data goes
DograhOne Dograh connection manages LLM, STT and TTSTo Dograh's hosted service (default MPS_API_URL is services.dograh.com)
BYOKSeparate connections for LLM, STT and TTS: OpenAI, Groq, Deepgram, ElevenLabs and many moreTo each provider you pick
RealtimeA speech-to-speech provider plus a separate LLMTo those providers

The docs warn that providers receive prompts, history and audio; read their retention and training policies.

For fully local, use BYOK with the provider Local Models (Speaches) for each job. Dograh accepts any OpenAI-compatible endpoint: Ollama or vLLM for the LLM, Speaches for speech. Our guides cover the parts: Ollama, Speaches speech to text and Kokoro Web (another OpenAI-compatible voice server).

The simplest layout puts both servers on Dograh's Docker network: no extra host ports, no localhost mistakes. Add this under services: in docker-compose.override.yaml (images pinned as in our guides, which also cover GPUs):

File contentyaml
  ollama:
    image: ollama/ollama:0.40.11
    restart: unless-stopped
    volumes:
      - ./models/ollama:/root/.ollama
    networks:
      - app-network
  speaches:
    image: ghcr.io/speaches-ai/speaches:0.8.3-cpu
    restart: unless-stopped
    environment:
      - WHISPER__TTL=300
    volumes:
      - ./models/hf-cache:/home/ubuntu/.cache/huggingface/hub
    networks:
      - app-network

Linux#

Run in terminalbash
mkdir -p models/ollama models/hf-cache
sudo chown -R 1000:1000 models/hf-cache
docker compose up -d
docker compose exec ollama ollama pull llama3.2
docker compose exec speaches curl -X POST http://localhost:8000/v1/models/Systran/faster-whisper-small
docker compose exec speaches curl -X POST http://localhost:8000/v1/models/speaches-ai/Kokoro-82M-v1.0-ONNX

The chown is needed because Speaches runs as uid 1000 (see our Speaches guide).

Windows (PowerShell)#

Run in terminalpowershell
docker compose up -d
docker compose exec ollama ollama pull llama3.2
docker compose exec speaches curl -X POST http://localhost:8000/v1/models/Systran/faster-whisper-small
docker compose exec speaches curl -X POST http://localhost:8000/v1/models/speaches-ai/Kokoro-82M-v1.0-ONNX

Mac (Terminal)#

Same commands as Linux, without chown. Docker on a Mac cannot use the Apple GPU, so models run on the CPU.

In Models, create a BYOK configuration with these values (field names and defaults are from Dograh's provider code at 1.49.0):

JobProviderModelBase URL
LLMLocal Models (Speaches)llama3.2http://ollama:11434/v1
STTLocal Models (Speaches)Systran/faster-whisper-small, language enhttp://speaches:8000/v1
TTSLocal Models (Speaches)speaches-ai/Kokoro-82M-v1.0-ONNX, voice af_hearthttp://speaches:8000/v1

Leave the API keys empty (the docs say self-hosted servers usually need none). Make it the organization default and repeat the Web Call.

Check it worked. The call works and docker compose logs api --tail 50 shows traffic to ollama and speaches, not to services.dograh.com. We did not test this chain. The Kokoro model name comes from the current Speaches docs and may differ in 0.8.3; docker compose exec speaches curl http://localhost:8000/v1/models lists the ids your server really has.

Tip: Dograh's local defaults are http://localhost:11434/v1 and http://localhost:8000/v1. Inside the API container localhost is the container itself, and 8000 is Dograh's own port. Replace both.

Step 7 (optional): Connect a phone provider#

Skip this if browser calls are enough. Real calls need your provider's credentials and a public HTTPS address the provider can reach.

Linux#

Open /telephony-configurations, click Add configuration, choose the provider (Twilio, Vonage, Plivo, Telnyx and others), enter the credentials and save. Add a phone number in E.164 format (+, country code, number). For inbound calls, assign an Inbound workflow to it.

Windows (PowerShell)#

The same screens in your browser; nothing runs in PowerShell.

Mac (Terminal)#

The same screens in your browser.

Check it worked. An outbound test call rings your own phone. The public address you gave the provider must match PUBLIC_BASE_URL exactly (HTTPS section).

Tip: Dograh does not sell phone service. Read the provider's price page before building; every minute is billed by them, and cloud STT, LLM and TTS add their own. We list no prices because Dograh publishes none.

Costs, privacy and the law#

Costs. The software is free. You pay for the machine, the phone provider and any cloud AI service. The default Dograh mode runs on credits of the hosted service (a user in issue #736 shows a key with 500 credits; the docs state no amounts).

Privacy. In cloud modes your prompts, transcripts and audio go to those providers. Every call stores a transcript and recording in your MinIO volume. Local models keep call content on your machine.

The law. General information, not legal advice. Twilio's help pages say US federal and state laws and EU laws apply to recording calls, some places need one participant's consent and others every participant's, and you should ask legal counsel. The FCC ruled on 8 February 2024 that calls made with AI-generated voices are "artificial" under the US Telephone Consumer Protection Act. Tell callers they speak to an AI and are recorded, get consent where required, and check the rules where your callers live.

Put it behind HTTPS (with or without Traefik)#

Use this for a server with a domain or phone webhooks; local-only users can skip it. The routes mirror the project's own nginx file: /api/v1/ to the API (with WebSockets), /voice-audio/ to MinIO, the rest to the UI.

One limit. The proxy carries the UI, API, phone WebSocket and recordings, not the WebRTC audio of browser Web Calls. The docs ask for TCP 3478 and 5349 and UDP 3478, 5349 and 49152-49200 for that, and offer setup_remote.sh (own nginx and coturn) as the route. We have not tested Web Calls through a proxy.

Add these lines to .env (from the project's remote setup script), then run docker compose up -d:

File contenttext
ENVIRONMENT=production
PUBLIC_HOST=voice.example.com
PUBLIC_BASE_URL=https://voice.example.com

With Traefik#

On a server that already runs Traefik v3 with the Docker provider, add labels and join the external proxy network. This assumes an entrypoint called https. Create the network if missing:

Run in terminalbash
docker network create proxy

Merge this into docker-compose.override.yaml (the ui and api keys join the entries you have; minio is new). Traefik prefers longer rules, so the path routers win over the plain host router:

File contentyaml
services:
  ui:
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.dograh-ui.rule=Host(`voice.example.com`)"
      - "traefik.http.routers.dograh-ui.entrypoints=https"
      - "traefik.http.routers.dograh-ui.tls=true"
      - "traefik.http.services.dograh-ui.loadbalancer.server.port=3010"
  api:
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.dograh-api.rule=Host(`voice.example.com`) && PathPrefix(`/api/v1/`)"
      - "traefik.http.routers.dograh-api.entrypoints=https"
      - "traefik.http.routers.dograh-api.tls=true"
      - "traefik.http.services.dograh-api.loadbalancer.server.port=8000"
  minio:
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.dograh-audio.rule=Host(`voice.example.com`) && PathPrefix(`/voice-audio/`)"
      - "traefik.http.routers.dograh-audio.entrypoints=https"
      - "traefik.http.routers.dograh-audio.tls=true"
      - "traefik.http.services.dograh-audio.loadbalancer.server.port=9000"

networks:
  proxy:
    external: true

Check it worked. curl -I https://voice.example.com/ returns an HTTP status line over HTTPS and the UI opens with a valid certificate. We did not run this.

Without Traefik: Caddy#

Choose this if you have no Traefik. Caddy gets and renews certificates by itself. Step 3 published the API, UI and MinIO on 127.0.0.1, so Caddy on the same machine reaches them without Docker networks.

CaseWhat you need
Public domainDNS A record to the server, ports 80 and 443 open. Caddy gets a Let's Encrypt certificate automatically.
Home onlyA local name pointing at the server, and tls internal (Caddy's own certificate, trusted by each device).

Install Caddy from the official install page.

Linux#

Run in terminalbash
sudo apt install --yes debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo apt install caddy

Put the Caddyfile in /etc/caddy/Caddyfile, then sudo systemctl reload caddy.

Windows (PowerShell)#

choco install caddy (or scoop install caddy), save the Caddyfile in the Dograh folder, then caddy run --config Caddyfile.

Mac (Terminal)#

Run in terminalbash
brew install caddy
caddy run --config Caddyfile

The Caddyfile for a public domain:

File contenttext
voice.example.com {
    handle /api/v1/* {
        reverse_proxy 127.0.0.1:8000
    }
    handle /voice-audio/* {
        reverse_proxy 127.0.0.1:9000
    }
    handle {
        reverse_proxy 127.0.0.1:3010
    }
}

For a home-only name, add tls internal inside the block (for example voice.home.arpa) and run caddy trust on the Caddy machine. For other devices, copy Caddy's pki/authorities/local/root.crt and trust it. These OS steps are not from the Caddy docs: on Linux copy it to /usr/local/share/ca-certificates/caddy-root.crt and run sudo update-ca-certificates; on Windows (administrator PowerShell) certutil -addstore -f "ROOT" caddy-root.crt; on Mac sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain caddy-root.crt.

Check it worked. curl -I https://voice.example.com/ (Windows: curl.exe -I) returns an HTTP status line over HTTPS. With tls internal the padlock appears only after the root is trusted. A failed Let's Encrypt certificate usually means the DNS record or port 80.

Tip: If webhook signatures fail behind Caddy, compare the URL your provider calls with PUBLIC_BASE_URL character by character.

Security and testing#

Dograh holds prompts, call recordings and provider keys, so treat it like any app with secrets.

ControlHow
SignupENABLE_SIGNUP=false after your first account
SecretsOnly in .env, chmod 600. The compose fallbacks (postgres, redis, minioadmin) apply only if .env is missing
PortsNo host ports for Postgres and Redis; API and UI on 127.0.0.1; outside access only through HTTPS
FirewallThe compose file warns the API trusts forwarded headers from any peer, so firewall port 8000
Phone webhooksSignatures are checked per provider; TELEPHONY_WS_TOKEN_SECRET can sign the media WebSocket URL

Quick tests (Windows: same lines, with curl.exe -I):

Run in terminalbash
docker compose ps
docker compose logs api --tail 100
curl -I https://voice.example.com/

Common mistakes#

These come from the official files, the docs' warnings, open issues and common AI answers.

MistakeWhy it happensDo this instead
Running docker compose up on the downloaded file aloneIt needs secrets; its header says it "will fail or come up misconfigured" and OSS_JWT_SECRET is requiredRun the official script first (Step 2)
Answering Y to "Start Dograh now?"The script starts the tunnel profile: a public *.trycloudflare.com address to your APIAnswer n, then docker compose up -d
Leaving Postgres and Redis openThe official file publishes 5432 and 6379 on every interfaceUse the override in Step 3
Typing localhost for Ollama or SpeachesInside the API container localhost is the container; the defaults are wrong in Docker and 8000 is Dograh's own portUse service names such as http://ollama:11434/v1
Silence in a Web Call, log says "Data channel not established within 10s after connection"Reported on Windows with Docker Desktop (issue #800) and on a Linux server (#811); Test Chat still worksTry the docs' setup_local script, which adds coturn, and a second browser
Old dograh-ui with a new dograh-apiThe docs warn the UI expects matching API responsesChange both tags together
Editing .env, then docker compose restartThe README says restart does not apply .env changesUse docker compose up -d
A proxy that rewrites URLs in front of phone webhooksProviders sign the full URL (webhooks docs; issue #830 for Twilio region ie1)Keep PUBLIC_BASE_URL identical to the provider's URL
AI assistants: docker run dograh/dograh or one containerThey guess one image; Dograh is five containers and the images are dograh-api and dograh-uiUse the official compose file and script (Steps 2 and 3)
AI assistants: "no keys needed, so it is all local"The README says no keys are needed because Dograh's hosted stack does the workUse BYOK with local models (Step 6)
AI assistants: port 3000 or a v1.49.0 Docker tagThe UI is on 3010 and Docker tags are bare (1.49.0; dograh-v1.49.0 is the GitHub tag)Use the ports and tags in this guide

Tip: After every start, read docker compose logs api --tail 100. Most problems, from a missing secret to a refused model, print there in plain words.

Tip: Test Chat first. If chat works and Test Audio does not, your models and prompts are fine and the problem is browser audio or networking.

Tip: Local models answer slower than hosted ones. Start with a small model and measure before choosing a bigger one.

Troubleshooting#

ProblemSolution
Port 3010 or 8000 in uselsof -i :3010 (Linux, Mac) or netstat -ano | findstr :3010 (PowerShell); stop that program or change the published port in the override
OSS_JWT_SECRET must be set to a strong secret.env is missing or has no secret; run the Step 2 script again, it keeps existing values
Login or UI failsdocker compose ps (the API must be healthy), then docker compose logs api --tail 100
No audio in Web CallSee "Data channel not established" below
Phone call never reaches the agentThe provider needs your public HTTPS address; check the number format and the provider's error log

Data channel not established within 10s after connection#

Seen on Windows with Docker Desktop (issue #800) and on a Linux server with coturn (issue #811). The #811 reporter traced it to ICE: on a one-IP server the pair that looks "completed" uses a private Docker address the browser cannot reach, and coturn refuses the relay pair. That is a user's analysis, not a maintainer statement. The docs offer the setup_local script (adds coturn) and FORCE_TURN_RELAY=true in .env to prove media really uses TURN; set it back to false afterwards.

Tool messages found but no tools provided#

File contenttext
openai.BadRequestError: Error code: 400 - Tool messages found but no tools provided. Tools must be specified if tool calls or tool messages are present.

Issue #841 reports this from strict OpenAI-compatible servers (vLLM, Speaches) at the end-call node. A fix is linked there; read your release notes.

Known bugs (open, October 2026)#

  • #800, #811: WebRTC data channel never established (above).
  • #736: STT answers HTTP 402 with the Dograh-managed key.
  • #852: the agent goes silent mid-call although the LLM returns 200.

Updates and backups#

Update. Read the release notes, set both image tags in docker-compose.override.yaml to the same new version (no v), then:

Run in terminalbash
docker compose pull
docker compose up -d
docker compose ps --format "table {{.Service}}\t{{.Image}}"

The last line shows the running tags; api and ui must match. The docs warn that rolling back across a database migration is not always safe, so back up first.

Back up the database, the recordings and .env. Check volume names with docker volume ls first; ours follow the project name dograh.

Linux#

Run in terminalbash
cd ~/docker_volumes/dograh
D=$(date +%F)
docker compose exec -T postgres pg_dump -U postgres -Fc -f /tmp/dograh.dump postgres
docker compose cp postgres:/tmp/dograh.dump backups/dograh-$D.dump
docker run --rm -v dograh_minio-data:/data -v "$PWD/backups:/backup" alpine tar czf /backup/minio-$D.tar.gz -C /data .
cp .env backups/env-$D && chmod 600 backups/env-$D

Save these lines as a script and run it at 03:00 with crontab -e: 0 3 * * * /path/to/backup.sh.

Windows (PowerShell)#

Codepowershell
Set-Location "$HOME\docker_volumes\dograh"
$d = Get-Date -Format yyyy-MM-dd
docker compose exec -T postgres pg_dump -U postgres -Fc -f /tmp/dograh.dump postgres
docker compose cp postgres:/tmp/dograh.dump "backups\dograh-$d.dump"
docker run --rm -v dograh_minio-data:/data -v "${PWD}\backups:/backup" alpine tar czf "/backup/minio-$d.tar.gz" -C /data .
Copy-Item .env "backups\env-$d"

Run a saved script daily at 03:00 with Task Scheduler.

Mac (Terminal)#

Same commands as Linux, with crontab -e for 03:00.

Restore test. Restore into a throwaway database, never over the live one:

Run in terminalbash
docker compose cp backups/dograh-<date>.dump postgres:/tmp/restore.dump
docker compose exec -T postgres createdb -U postgres restoretest
docker compose exec -T postgres pg_restore -U postgres -d restoretest /tmp/restore.dump
docker compose exec -T postgres psql -U postgres -d restoretest -c "\dt"
docker compose exec -T postgres dropdb -U postgres restoretest

The table list should not be empty. We did not run these; they are standard PostgreSQL tools.

Performance and resources#

The docs give limits, not measurements: 8 GB RAM (4 GB for Docker), 2 cores and 10 GB disk locally; 8 GB and 4 vCPUs for a remote server. We measured nothing, so we give no RAM figure.

  • Watch it: docker stats shows CPU and memory per container.
  • More calls: the docs describe several API workers (FASTAPI_WORKERS) behind nginx; see their Scaling page.
  • Call speed: each turn adds STT, LLM and TTS time. With OpenRouter, set a provider order: the default spreads requests to the cheapest provider and can add delay (docs).

FAQ#

Is Dograh free? The software is free under BSD 2-Clause. You pay for phone minutes and any cloud AI service you use.

Can it run fully offline? Only with BYOK and local models for all three jobs, and no phone provider. The default Dograh mode needs the hosted service.

Can I use my own phone system? Yes: the docs list Asterisk ARI and bring-your-own SIP, plus providers such as Twilio.

Does it send data to the project? Anonymous telemetry unless ENABLE_TELEMETRY=false. In Dograh mode your call content also goes to the hosted service.

Sources#

Checked against the official docs on 2026-10-31.