Skip to content

Self-host Provon With Node

The Node runtime is the default self-hosting path. One process serves the Workbench, API, Gateway, OTLP ingest, background loops, and optional local model runtime.

View as Markdown Open the plain-text version of this page.
On this page Browse sections

Use it for a VM, bare-metal host, Docker host, private cloud, or a single-instance container platform. Read Self-hosting architecture before adding replicas: the default queues and runtime state are local to one data directory.

Requirements#

  • Node.js 22 or newer and pnpm 10 for a source deployment, or Docker for a container deployment.
  • A durable filesystem for metadata, local telemetry, blobs, queue state, and model artifacts.
  • An HTTPS reverse proxy or platform load balancer for public traffic.
  • Outbound access to enabled model providers, identity providers, and connectors.
  • A stable production AUTH_SECRET.

For production, allocate storage from expected trace volume and payload capture policy rather than from metadata size. Prompt bodies, tool results, attachments, and retained raw OTLP payloads usually dominate growth.

Option A: Docker#

Build the checked-in production image:

bash
docker build --target runtime -t provon:local .
docker volume create provon-data

Generate and store a secret in your secret manager:

bash
openssl rand -base64 32

Start one instance with local SQLite, DuckDB, blobs, and runtime state on the durable volume:

bash
export AUTH_SECRET="replace-with-secret-manager-value"

docker run -d \
  --name provon \
  --restart unless-stopped \
  -p 127.0.0.1:3000:3000 \
  -v provon-data:/data \
  -e NODE_ENV=production \
  -e AUTH_SECRET \
  -e PROVON_HTTP_HOST=0.0.0.0 \
  -e PROVON_HTTP_PORT=3000 \
  -e PROVON_META_DB_URL=libsql:/data/meta.db \
  -e PROVON_TELEMETRY_DB_URL=duckdb:/data/telemetry.duckdb \
  -e PROVON_LOCAL_BLOB_DIR=/data/blobs \
  -e PROVON_SELF_HOSTED_DIR=/data/self-hosted \
  -e PROVON_AUTH_TRUST_HOST=true \
  -e PROVON_WORKBENCH_ORIGINS=https://provon.example.com \
  provon:local

Keep the host port bound to loopback when a reverse proxy runs on the same host. On a container platform, expose port 3000 only through the platform ingress.

The image runs as uid and gid 1001. If you replace the named volume with a bind mount, make its directories writable by that identity before starting the container.

About Docker Compose#

The checked-in docker-compose.yml is a development and integration topology. It:

  • builds the same Node image;
  • uses MotherDuck for telemetry;
  • starts the example TypeScript agent;
  • must be adapted to the selected MotherDuck isolation mode and credentials.

Do not treat it as a production manifest without removing the example workload, defining secret delivery, adding durable backup policy, and configuring the public origin. For shared MotherDuck, set PROVON_TELEMETRY_TENANT_ISOLATION=shared in the service environment and provide MOTHERDUCK_TOKEN; organization isolation requires MOTHERDUCK_ADMIN_TOKEN.

Option B: Run From Source#

Install and build:

bash
pnpm install --frozen-lockfile
pnpm --filter @provon/node-server build

Set production configuration:

bash
export NODE_ENV=production
export AUTH_SECRET="replace-with-secret-manager-value"
export PROVON_HTTP_HOST=127.0.0.1
export PROVON_HTTP_PORT=3000
export PROVON_META_DB_URL="libsql:/srv/provon/meta.db"
export PROVON_TELEMETRY_DB_URL="duckdb:/srv/provon/telemetry.duckdb"
export PROVON_LOCAL_BLOB_DIR="/srv/provon/blobs"
export PROVON_SELF_HOSTED_DIR="/srv/provon/self-hosted"
export PROVON_AUTH_TRUST_HOST=true
export PROVON_WORKBENCH_ORIGINS="https://provon.example.com"

Start the deployable entrypoint:

bash
pnpm --filter @provon/node-server start

Use a process supervisor that:

  • restarts the process after failure;
  • sends SIGTERM for graceful shutdown;
  • allows enough time for HTTP connections and background loops to stop;
  • captures stdout and stderr;
  • starts only one active instance for the data directory.

The server applies metadata migrations before it begins listening. A migration failure stops startup instead of serving the new application against an old schema.

Choose Telemetry Storage#

Local DuckDB#

bash
PROVON_TELEMETRY_DB_URL=duckdb:/srv/provon/telemetry.duckdb

Choose DuckDB for the simplest fully local deployment. Keep the database on a local durable disk, not an eventually consistent network filesystem. One Node process should own the local file.

MotherDuck#

bash
PROVON_TELEMETRY_DB_URL=motherduck:provon-telemetry
MOTHERDUCK_TOKEN=...

Choose MotherDuck when telemetry should use managed remote storage while the Provon runtime remains self-hosted. Metadata, blobs, and local queue state still require durable Node storage.

Organization-isolated MotherDuck provisioning uses MOTHERDUCK_ADMIN_TOKEN; shared mode uses MOTHERDUCK_TOKEN. Review the complete variables in Self-hosting configuration.

Configure The Reverse Proxy#

Keep Workbench, API, Gateway, and OTLP on one public origin:

text
https://provon.example.com/
https://provon.example.com/v1/*
https://provon.example.com/gateway/v1/*
https://provon.example.com/api/auth/*

The proxy must:

  • terminate HTTPS;
  • preserve request bodies and streaming responses;
  • support WebSocket upgrades for realtime Gateway paths;
  • set X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Port;
  • allow the configured OTLP body-size ceiling;
  • use timeouts suitable for long model responses.

Set PROVON_AUTH_TRUST_HOST=true only when the proxy overwrites forwarded headers and untrusted clients cannot inject them. Set PROVON_WORKBENCH_ORIGINS to the exact browser origin.

OAuth callbacks use the public origin:

text
https://provon.example.com/api/auth/callback/github
https://provon.example.com/api/auth/callback/google
https://provon.example.com/v1/integrations/github/callback

Verify The Deployment#

Check runtime liveness:

bash
curl --fail --silent https://provon.example.com/healthz

The response includes status, version, and deploymentTarget. This endpoint proves the HTTP runtime is alive; it does not query every storage or provider dependency.

Then verify the product path:

  1. Sign in and create an organization and project.
  2. Create a project API key.
  3. Send one OTLP trace and confirm it appears in Traces.
  4. Send one Gateway request and confirm both the response and provider-attempt trace.
  5. Run one diagnostic Rule and inspect the Run.
  6. If enabled, connect GitHub and create one repair Issue from a supported Finding.
  7. Restart the process and confirm metadata, telemetry, blobs, and pending work remain available.

Use the Quickstart, Tracing quickstart, and Gateway quickstart for the request examples.

Scale And Availability#

The default Node deployment favors operational simplicity over horizontal availability.

  • Run one active process.
  • Use host or platform restart policy for process recovery.
  • Put all local state on durable storage.
  • Back up metadata, telemetry, blobs, and runtime state as one recovery set.
  • Move telemetry to MotherDuck only when remote telemetry storage is useful; it does not externalize local queues.
  • Choose the Cloudflare runtime when Gateway, ingest, and jobs must scale independently.

Next Steps#