Skip to main content
Passport ships as one container image. The same image runs locally and in production; everything is configured from environment variables, so there is nothing secret baked into the image. In self-hosted mode the hosted service never touches your data.

Build and run

There is no public pull-by-tag registry image today. Build from this repository, then run the container:
Add -e DATABASE_URL='postgres://…' for durable multi-replica Postgres. Without it, state lives under /data and must be on a mounted volume. For production bring-up checklists beyond this page, see docs/DEPLOY.md and docs/OPERATIONS.md in the repository.

What you need

The container

One Passport container, listening on PORT (default 8790).

Postgres

A Postgres database for durable, backed-up state that survives redeploys. Without it, state lives in a JSON file you must persist on a mounted volume.

Production posture the server enforces at boot

When NODE_ENV=production, Passport fails closed: a misconfigured deployment refuses to boot rather than run with plaintext secrets, open unverified signup, or mock data on a real domain. Startup exits with a named error unless all of the following hold:
  • PASSPORT_ENCRYPTION_KEY is set. Without it, upstream OAuth tokens and company keys would be stored in plaintext.
  • PUBLIC_URL is set and starts with https://. Otherwise OAuth callbacks and CORS would trust the Host header.
  • PASSPORT_DEMO is explicit, either off (recommended) or on.
  • If signup is enabled, configure either verified email or Google/GitHub identity login. With PASSPORT_EMAIL_VERIFY=off, production becomes provider-only: typed email addresses cannot create sessions or workspaces.
A single escape hatch, PASSPORT_UNSAFE_OVERRIDE=i-accept-the-risk, bypasses these checks for a throwaway staging bring-up. It logs loudly every time it is used. Do not use it in production.

Core

Persistence

Security and encryption

Key rotation. Set PASSPORT_ENCRYPTION_KEY to the new key, move the old key into PASSPORT_ENCRYPTION_KEYS, deploy, then from the repository run cd server && npm run rekey to re-encrypt every stored secret under the new primary, then drop the old key. A value whose key is absent is left untouched, never overwritten, so nothing is lost mid-rotation.

Signup and demo

Email

Email is provider-agnostic. Setting a transport enables features that require email (verified sign-in, invites, request notifications).

Single sign-on (OIDC) fallback

Per-workspace SSO is configured in Settings (see SSO). These environment variables provide a global fallback for the single-tenant or appliance case.

Connecting upstream accounts

You can also let a person sign into Passport itself with Google or GitHub by setting the PASSPORT_LOGIN_GOOGLE_* and PASSPORT_LOGIN_GITHUB_* pairs. A provider is available only when both its client ID and secret are set.

Billing (Stripe)

Leave STRIPE_SECRET_KEY unset on a self-hosted appliance: with no key, every workspace gets everything unlocked and billing does not exist.

Gateway rate limits

Per-workspace and per-token caps on the gateway, plus a per-replica concurrency guard. These fail open: a rate-limiter error never blocks the gateway.

Metrics, monitoring, and paging

Sentry is entirely optional for a self-hosted deployment. With both DSNs unset, no Sentry telemetry leaves the instance. VITE_* and the browser copy of SENTRY_RELEASE are image-build inputs. For source maps, first run the web build locally with SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT, SENTRY_REPOSITORY, and the same release, then build the image without the token. Omitting that local upload simply means browser frames have no source maps. The entrypoint removes any platform-injected runtime token before Node starts, and only public browser settings are compiled into assets. When enabled, Passport sends normalized route/status/latency data, stack traces, operational spans/logs/metrics, MCP app/tool/client identifiers and outcomes, and opaque member/workspace IDs. Collection of query strings, cookies, authorization headers, HTTP bodies, database statements, MCP inputs/outputs, and AI content is disabled. Passport does not deliberately attach names or emails, and its scrubbers redact email-like values and credential patterns found in diagnostic text. Browser Replay is intentionally disabled because its rrweb metadata can serialize hash/query URLs before event hooks can scrub them. Do not enable it until URL metadata can be guaranteed safe before serialization. Sentry retention is controlled by your Sentry plan and organization settings, independently of Passport’s event-log retention. For staging verification of Sentry releases and source maps, see docs/DEPLOY.md and docs/OPERATIONS.md in the repository.

Advanced tuning

These have sensible defaults and rarely need changing.
Health checks: GET /healthz returns {"ok":true} when the process is up. GET /readyz runs a cheap storage ping (SELECT 1 on Postgres) and returns 503 if storage is unreachable. When Postgres is down, Passport fails closed on policy: mutating requests fail (and failed commits return 503 with retry guidance) rather than bypass controls. Some read-only paths may still serve a stale in-memory snapshot until connectivity returns.