Skip to content

Self-Hosting

Edit on GitHub

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 with the pgvector extension (the bundled compose file uses pgvector/pgvector:pg17). On managed Postgres the connecting role must be allowed to CREATE EXTENSION for vector, pg_trgm, pgcrypto, and uuid-ossp, or migrations fail at startup. Already running the bundled Postgres on a populated volume? See Upgrading Postgres to 17 before you pull.
  • 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.15.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, SESSION_ENCRYPTION_KEY and INSTANCE_ADMIN_EMAILS, then add a sign-in provider through the setup URL the server logs at startup — see Authentication. Until a provider is enabled, nobody can 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:

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

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

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. People sign in through Google, GitHub, or any number of generic OIDC providers (Keycloak / Zitadel / Auth0 / Okta / …).

Since v0.15.0 the sign-in providers are not environment variables: they are stored in the database and managed in the admin area under Settings → Authentication. A new instance has none, so it logs a one-time setup URL at startup. Set these, start the instance, and open that URL to add your first provider:

Terminal window
SESSION_ENCRYPTION_KEY=<64 hex> # encrypts the vx_session cookie
INSTANCE_ADMIN_EMAILS=you@example.com # the root admin who finishes setup
Terminal window
docker compose logs app | grep "SETUP URL"

Upgrading an instance that used AUTH_PROVIDER and the provider variables? They are imported at the first start and then ignored: see the upgrade note.

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 first-run setup, per-provider setup, the access allowlist, the embedded Authorization Server, and the HTTPS expectation. Locked out? See Lockout Recovery.

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

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 Not an env var since v0.15.0: an instance admin configures SMTP, Mailgun, Postmark or SendGrid in the admin area under Settings → Email (setup). The EMAIL_PROVIDER / SMTP_* env vars are deprecated and only imported at startup while nothing is stored (upgrade note) Instance mail is discarded
Analytics VITE_GTM_ID / VITE_GA4_MEASUREMENT_ID (Google Tag Manager / GA4). Setting VITE_GTM_ID is the opt-in; there is no separate enable flag, and VibeXP ships no cookie-consent gate of its own No analytics
Private-network outbound calls OUTBOUND_ALLOWED_CIDRS (comma-separated, e.g. 172.16.0.0/12) so the SSRF guard may reach a self-hosted embedding or model sidecar Loopback and private destinations are refused
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

The “VibeXP” name and logo are the project maintainer’s brand. To show your own company name, logo and support and legal links in the app and in its emails, sign in as an instance admin and fill in Settings → Site info in the admin area: see Site Info (Branding). A save applies without a restart or a rebuild, and VibeXP stays credited as “Powered by VibeXP”. Since v0.15.0 no environment variable sets the branding. The independent static sites (website, blog, docs) have their own VITE_* / PUBLIC_* env vars in their repos.