Self-hosting¶
Beta — Venkai runs entirely on your own infrastructure. No component calls out to a Venkai-operated service, and the semantic embedding model runs locally.
Prerequisites¶
| Docker + Compose | v2 |
| PostgreSQL | Provided by the production compose, or bring your own |
| Domain + TLS | Caddy obtains certificates automatically |
| Outbound internet | Build time only — the embedding model is baked in |
Procedure¶
1. Checkout and configure¶
git clone <your-venkai-checkout> && cd venkai
cp .env.example .env
git check-ignore -v .env # must print a .gitignore line before you continue
Fill in the two required secrets:
python -c "import secrets; print('VENKAI_JWT_SECRET=' + secrets.token_urlsafe(64))" >> .env
python -c "import secrets; print('POSTGRES_PASSWORD=' + secrets.token_urlsafe(32))" >> .env
Set VENKAI_ENV=production and VENKAI_ALLOWED_ORIGINS to your dashboard
origin. Full table: Environment variables.
2. Build the image before anything else¶
Do this as a separate, explicit step. A dependency conflict once meant the image had never built successfully while the stack was documented as ready. Prove the artefact exists before trusting a runbook — including this one.
3. Create the external network (dev compose only)¶
docker-compose.yml declares it external: true, so compose will not create
it. The production compose does not need this.
4. Migrate¶
The production compose includes a migrate service that runs Alembic once and
exits. There is no automatic migration on startup — a stack that silently
migrates can silently migrate the wrong way.
5. Start¶
docker compose -f docker-compose.prod.yml up -d --build
docker compose -f docker-compose.prod.yml ps
6. Verify — from outside the host¶
{"detail": "database unreachable: …"} with 503 means the API is up and its
store is not.
Then prove the round trip actually works, which a health check does not:
# register → key → write → read
curl -sX POST https://your-domain/api/auth/register -H 'Content-Type: application/json' \
-c c.txt -d '{"email":"you@example.com","password":"a-real-password","organization":"Acme"}'
KEY=$(curl -sX POST https://your-domain/api/auth/api-keys -b c.txt \
-H 'Content-Type: application/json' -d '{"label":"smoke"}' \
| python -c 'import json,sys; print(json.load(sys.stdin)["api_key"])')
curl -sX POST https://your-domain/api/memory -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' \
-d '{"project_id":"smoke","agent_id":"smoke","content":"self-hosting smoke test","type":"fact"}'
curl -sG https://your-domain/api/context/smoke/relevant -H "Authorization: Bearer $KEY" \
--data-urlencode 'query=smoke test'
A memory in and the same memory out is the real proof of a working deployment.
Networking¶
flowchart LR
I["Internet"] --> C["Caddy :80 :443<br/>TLS termination"]
C -->|"/api/*"| A["api :8100<br/><b>not host-published</b>"]
C -->|"/"| D["static dashboard"]
A --> P[("postgres :5432<br/>internal network only")]
Port 8100 is deliberately not published to the host. Publishing it lets a
client bypass Caddy and present its own X-Forwarded-For, which would spoof the
rate limiter and the logs. Keep it internal.
For local debugging only, temporarily add ports: ["8100:8100"] — and remove
it afterwards.
Bring your own database¶
Then drop the db service. Requirements: PostgreSQL, a user that can create
tables for the migration, and network reachability from the API container. No
extensions are required — there is no pgvector dependency
(Storage).
Backups¶
Venkai provides none. This is your database and your policy.
# dump
docker compose -f docker-compose.prod.yml exec -T db \
pg_dump -U venkai venkai | gzip > venkai-$(date +%F).sql.gz
# restore
gunzip -c venkai-2026-08-18.sql.gz | \
docker compose -f docker-compose.prod.yml exec -T db psql -U venkai venkai
Also available at the application level: GET /api/export returns an
organization's data as JSON.
Test the restore, not the dump
An untested backup is a hypothesis. Restore into a scratch database and run the smoke test above against it.
Upgrades¶
git pull
docker build -t venkai-api . # 1. prove it builds
docker compose -f docker-compose.prod.yml run --rm migrate # 2. migrate
docker compose -f docker-compose.prod.yml up -d --build # 3. roll
curl -fsS https://your-domain/api/health # 4. verify
Back up before step 2. Alembic migrations are not automatically reversible.
Rollback¶
git checkout <previous-tag>
docker build -t venkai-api .
docker compose -f docker-compose.prod.yml up -d --build
If the failed release included a migration, restore the database dump. A forward-only migration cannot be undone by redeploying the old image — the old code will meet the new schema.
What you own¶
| Database backups and restores | You |
| TLS certificates | Caddy, automatically |
| Monitoring and alerting | You |
| Log retention | You |
| Secret rotation | You — and rotating VENKAI_JWT_SECRET logs everyone out |
| Scaling | You — the app is stateless; the store is not |