Self-Hosting
VibeXP is designed to be self-hosted on your own infrastructure and domain. There is no committed secret or infrastructure-specific value in the repository — every deployment supplies its own configuration through environment variables (or its own config.yaml).
Prerequisites
Section titled “Prerequisites”- Docker + Docker Compose (for the quick start), or Node 20+ / Go 1.25+ for local dev.
- PostgreSQL 15/16 with the
pgvectorextension (the bundled compose file usespgvector/pgvector:pg16). - A login provider for production sign-in — Google, GitHub, or any OIDC provider (see Authentication) — or use the dev-login bypass for local evaluation.
- (For semantic search) an OpenAI-compatible embeddings endpoint configured in-app — see Search and embeddings. No external embedding service is required to boot.
Quick start
Section titled “Quick start”git clone https://github.com/vibexp/vibexp.gitcd vibexpdocker compose up -dThe root docker-compose.yml runs the published combined ghcr.io/vibexp/vibexp image plus a PostgreSQL (pgvector) database. The app and the API share one port: open http://localhost:8080 (API under /api/v1, same origin — no separate frontend URL). To configure it, edit the environment: block on the app service in docker-compose.yml before exposing it publicly (backend/.env is only used by the make-based local dev workflow, not by compose).
How configuration works
Section titled “How configuration works”The backend reads a single required config.yaml. The published image bakes a default at /app/config.yaml whose every value is a ${VAR:-default} reference, so environment variables alone configure a container — that is what the compose environment: block feeds. To control every setting, mount your own file over /app/config.yaml (start from backend/config.example.yaml). Compose is optional; with a reachable pgvector-enabled PostgreSQL, one container is enough:
docker run -p 8080:8080 \ -e DB_HOST=your-db-host -e DB_PASSWORD=secret \ -e ENCRYPTION_KEY="$(openssl rand -base64 24 | cut -c1-32)" \ -e FRONTEND_BASE_URL=http://localhost:8080 \ ghcr.io/vibexp/vibexp:0.9.0The localhost FRONTEND_BASE_URL enables the dev-login bypass so you can sign in immediately. For a real deployment, set FRONTEND_BASE_URL to your public URL and configure a login provider (AUTH_PROVIDER + its client credentials + SESSION_ENCRYPTION_KEY — see Authentication); otherwise the instance boots but has no way to sign in.
Your domains
Section titled “Your domains”The application’s URLs are yours — they are not hardcoded. Throughout this documentation we use the deployment-agnostic placeholder <your-vibexp-host> for your instance. The app, API, and MCP endpoint all live on that one origin (the combined image serves the SPA, /api/v1, and /mcp/v1/common from the same port — no CORS to configure). Set it per deployment:
| Concern | Env var | Notes |
|---|---|---|
| Public base URL | FRONTEND_BASE_URL | the single origin serving app + API; drives auth redirects + email links |
| MCP auth issuer | OAUTH_AS_ISSUER_URL | your public HTTPS URL; enables the embedded MCP OAuth server |
| MCP resource URI | MCP_RESOURCE_URI | your MCP endpoint, e.g. https://<your-vibexp-host>/mcp/v1/common |
The marketing website, blog, and docs-site are independent static sites; their public URLs and branding are configurable via their own VITE_* / PUBLIC_* env vars (see each service’s .env.example).
Required configuration (backend)
Section titled “Required configuration (backend)”These are the only hard requirements to boot the backend. Everything else is optional and stays disabled until configured.
| Env var | Required | Purpose |
|---|---|---|
DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAME | ✅ | PostgreSQL connection (must have pgvector) |
ENCRYPTION_KEY | ✅ | AES-256 at-rest key — exactly 32 bytes |
There is no embedding env var: embedding and model providers are configured per team in the app (see Search and embeddings).
Authentication
Section titled “Authentication”VibeXP is provider-agnostic and brings its own embedded OAuth 2.1 Authorization Server in-house to secure the MCP endpoint — no third-party auth service required. Choose Google, GitHub, or a generic OIDC provider (Keycloak / Zitadel / Auth0 / Okta / …) via AUTH_PROVIDER:
AUTH_PROVIDER=google # one of: google, github, oidcSESSION_ENCRYPTION_KEY=<64 hex> # encrypts the vx_session cookieTo enable several providers at once, mount your own config.yaml over /app/config.yaml with a provider list — the multi-provider auth.providers field is not settable via env vars:
auth: providers: ["google", "github"]For local evaluation without a provider, point FRONTEND_BASE_URL at localhost (the bundled compose file sets http://localhost:8080) — that enables the dev-login bypass, which only works on localhost. The bare image leaves FRONTEND_BASE_URL empty, which keeps dev-login off (fail-closed).
➡️ See Authentication & MCP Auth for per-provider setup, the embedded Authorization Server, the full env-var matrix, and the HTTPS expectation.
Search and embeddings
Section titled “Search and embeddings”Embeddings are generated in-process — an event-bus worker chunks, embeds, and stores content in pgvector. There is no external AI service to run and no AI_SERVICE_URL.
The embedding provider (any OpenAI-compatible embeddings endpoint: OpenAI, Ollama, vLLM, TEI, …) is configured per team, in-app, not via environment variables: Settings → Integration → Embedding Providers. Each provider stores the endpoint, encrypted API key, model id, chunk sizing, request concurrency, and optional query/document prefixes. Providers are validated on save and must return 1024-dimension vectors; the width is locked to the pgvector column and is not configurable.
The settings page also shows embedding coverage per team, with one-click Reprocess pending and Clear all embeddings actions. Changing a provider’s identity (endpoint or model) wipes and re-embeds that team’s data automatically.
Teams can also bring their own OpenAI-compatible LLM endpoints under Settings → Integration → Model Providers (encrypted API keys, connectivity validation on save).
Optional integrations
Section titled “Optional integrations”All disabled by default, enabled via env vars or a mounted config.yaml (see backend/config.example.yaml):
| Integration | Enable via | Behavior when off |
|---|---|---|
| Object storage (attachments) | GCS-compatible storage: GCS_RESOURCE_ATTACHMENTS_BUCKET (+ STORAGE_EMULATOR_HOST for an emulator) | Uploads return 503 |
EMAIL_PROVIDER (smtp, mailgun, postmark, sendgrid) + the provider’s credentials | Email features disabled | |
| Analytics | VITE_GTM_ENABLED + VITE_GTM_ID / VITE_GA4_MEASUREMENT_ID (Google Tag Manager / GA4) | No analytics |
| Telemetry | otel.* in a mounted config.yaml (any OTLP collector) | No telemetry |
| GitHub App | Not an env var: each team registers its own App in-app under Settings → GitHub Integration (setup) | Team has no GitHub integration |
Branding
Section titled “Branding”The “VibeXP” name and logo are the project maintainer’s brand. To white-label the app, set the branding env vars on the app service (VITE_SITE_NAME, VITE_SITE_URL, VITE_BRAND_LOGO_URL, …) — they are served to the SPA at runtime via /config.js, so a restart applies them without a rebuild. The independent static sites (website, blog, docs) have their own VITE_* / PUBLIC_* env vars in their repos.
Next steps
Section titled “Next steps”- Open Source — license model and where to file issues.
- Contributing — set up a dev environment and open a PR.