Skip to content

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).

  • Docker + Docker Compose (for the quick start), or Node 20+ / Go 1.25+ for local dev.
  • PostgreSQL 15/16 with the pgvector extension (the bundled compose file uses pgvector/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.
Terminal window
git clone https://github.com/vibexp/vibexp.git
cd vibexp
docker compose up -d

The 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).

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:

Terminal window
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.0

The 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.

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:

ConcernEnv varNotes
Public base URLFRONTEND_BASE_URLthe single origin serving app + API; drives auth redirects + email links
MCP auth issuerOAUTH_AS_ISSUER_URLyour public HTTPS URL; enables the embedded MCP OAuth server
MCP resource URIMCP_RESOURCE_URIyour 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).

These are the only hard requirements to boot the backend. Everything else is optional and stays disabled until configured.

Env varRequiredPurpose
DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAMEPostgreSQL connection (must have pgvector)
ENCRYPTION_KEYAES-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).

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:

Terminal window
AUTH_PROVIDER=google # one of: google, github, oidc
SESSION_ENCRYPTION_KEY=<64 hex> # encrypts the vx_session cookie

To 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.

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).

All disabled by default, enabled via env vars or a mounted config.yaml (see backend/config.example.yaml):

IntegrationEnable viaBehavior when off
Object storage (attachments)GCS-compatible storage: GCS_RESOURCE_ATTACHMENTS_BUCKET (+ STORAGE_EMULATOR_HOST for an emulator)Uploads return 503
EmailEMAIL_PROVIDER (smtp, mailgun, postmark, sendgrid) + the provider’s credentialsEmail features disabled
AnalyticsVITE_GTM_ENABLED + VITE_GTM_ID / VITE_GA4_MEASUREMENT_ID (Google Tag Manager / GA4)No analytics
Telemetryotel.* in a mounted config.yaml (any OTLP collector)No telemetry
GitHub AppNot an env var: each team registers its own App in-app under Settings → GitHub Integration (setup)Team has no GitHub integration

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.