Skip to main content
Production checklist Review the platform-neutral production topology before choosing a Compose path.

Docker

Use a repository checkout for the local demo. Production Compose downloads immutable assets and published images, so the server needs no checkout. The Compose service name redis and the REDIS_URL variable keep the protocol name for compatibility.
Start production with a fresh .env.example and database. Editing a demo .env leaves its public fixed sign-in code and sample data in place.

Compose topology commands

Topology is selected by files, not by an environment switch:
The core file sets SCHEDULER_DRIVER=none; both worker topologies set SCHEDULER_DRIVER=temporal. An environment value never adds or removes a service. The bundled topology owns its namespace retention. The external topology validates its namespace but never creates it or changes its policy.

How the stack starts

The deployment overlays contain no build: entries. Add compose.build.yaml to build web and worker from source. The migration job uses the same web image. The default stack starts PostgreSQL, Valkey, db-migrations, and the web app. The one-shot migration service runs npm run db:migrate. It applies the Prisma tree, then active deploy-blocking data migrations. No operator migration job is required. The server, worker, and /api/v1/health require finished rows with exact manifest checksums. With DEMO_FIXED_OTP=1, migrations also seed local demo data. Compose uses the version-pinned Docker Hub web image for migrations and the app. For source testing, override complete references with BISIBILITY_IMAGE and BISIBILITY_WORKER_IMAGE. If a migration fails partway, run npm run db:migrate again. Completed steps are skipped; only the failed step retries. Docker Compose keeps the app and worker stopped until the one-shot migration service exits successfully. services.migrations: "ready" requires all bundled Prisma migrations, finished active data migrations with expected checksums, and the public ID database contract. Run npx prisma migrate status in the deployed environment for detailed Prisma status. This default mode supports manual rank checks. For scheduled checks, choose a worker topology from Compose topology commands. The bundled topology adds Temporal Server 1.31.2 and the pinned Docker Hub worker image. Add --profile temporal-ui to expose Temporal Web UI at http://localhost:8233. An idempotent Admin Tools job creates or upgrades both schemas and the namespace before worker startup. For source testing, add -f compose.build.yaml --build; it builds both images and runs migrations from the web image. Use /api/v1/liveness for restarts and /api/v1/readiness for traffic; see Health endpoint exposure. Worker or Temporal failures can return 503 while web reads continue. The APP_REVISION build argument identifies the public commit; omitting it reports "unknown".

Local demo

bootstrap-local.sh writes generated secrets to a new .env. It sets DEMO_FIXED_OTP=1 and DEMO_INSTANCE_INSECURE_AUTH_ACK=1. Sign-in then accepts and displays a fixed code. Migrations seed demo data. Open http://localhost:3000 and sign in with demo@acme.dev and code 000000, also described under demo login. The script refuses to run when .env already exists, so it cannot overwrite a configured install.
Demo mode is insecure by design: anyone who can reach the app can sign in. Keep it on a throwaway local install and never expose it on a public address.
Seeded rank history is synthetic. Connect a SERP provider from Integrations before running a live check.

Production Compose

Download the immutable assets for one exact release into an empty directory. Do not run bootstrap-local.sh - it exists to enable demo mode in a repository checkout.
The generator refuses to overwrite .env. It creates independent secrets, sets both public origins, and sets DEPLOYMENT_ENV=production. Replace https://rank.example.com with the final origin, then review .env:
  1. Secrets. Back up BETTER_AUTH_SECRET and BISIBILITY_SECRETS_KEY; see Application secrets.
  2. Public URL. Confirm SITE_URL and BETTER_AUTH_URL are the final public origin. The template leaves both blank and Compose fails before starting if the generator has not filled them.
  3. Deployment stage. Keep DEPLOYMENT_ENV=production; see Operations.
  4. Email. Set EMAIL_PROVIDER and EMAIL_FROM so sign-in codes can be delivered. See Email delivery.
  5. Demo variables stay unset. See Fixed OTP.
  6. Client IP. See Trusted client IP.
Start the stack:
The empty database redirects the first visit to /setup. Create the instance admin, then connect a SERP provider from Integrations. Work through the production checklist before pointing real users at the instance. For scheduled checks with the bundled server, add both scheduling overlays:
The release manifest records SHA-256 hashes for every overlay, the environment template, and the generator. Publication verifies those bytes. docker-compose.self-host.yml remains a one-minor compatibility asset for the former --profile scheduled command.

Verify and troubleshoot startup

Check every service, the one-shot migration result, and the anonymous traffic probe before configuring a reverse proxy:
Add the worker and Temporal overlays to the first two commands when scheduled checks are enabled. See Health endpoint exposure for authenticated /api/v1/health diagnostics. For symptom-first diagnosis of startup failures, readiness, auth redirects, worker loops, schedules, client IP, and email delivery, see the canonical Troubleshooting runbook.

Use the CLI with this instance

Install the CLI on the workstation where you use bisibility:
Configure both URLs before browser sign-in. baseUrl is the REST API root. cloudUrl is the app origin for OAuth discovery and consent.
The CLI opens and prints the instance consent URL. Approval returns to a temporary workstation loopback listener. The CLI stores the reveal-once personal access token. Verify the instance, identity, and available projects:
The public origin must match SITE_URL and BETTER_AUTH_URL. Route /api/auth/* and /api/v1/* through the proxy. Run login on a workstation with a browser. On a headless host, provide a project API key or personal access token through the environment:
Environment variables take precedence over the CLI config file. See Authentication for choosing between project API keys and personal access tokens.

Access services from the host

The default stack publishes only the app on 127.0.0.1:3000; PostgreSQL and Valkey are internal to the Compose network. See Public port exposure. Core does not start the Temporal or worker overlays; the optional temporal-ui profile exposes the Web UI at 127.0.0.1:8233. For deliberate PostgreSQL or Valkey access from the Docker host, apply the opt-in debug override:
The override binds PostgreSQL to 127.0.0.1:5432 and Valkey to 127.0.0.1:6379. Change those loopback ports, or the app port, in .env when a port is already taken:
For permanent mappings, add the same loopback-bound ports to docker-compose.override.yml. Set a new app port in SITE_URL and BETTER_AUTH_URL, such as http://localhost:3100. A mismatch breaks auth callbacks and absolute links; Google sign-in and Search Console redirect URIs must use the same port. Give each local instance unique ports and a Compose project name, such as docker compose -p bisibility-staging up --build. Project names namespace volumes and keep databases separate.

Stop or reset the stack

down preserves the PostgreSQL volume, so the next up continues with the same data and applies only new migrations. down -v cannot be undone; use it when you want a clean install.

Examples

Runnable API, SDK, CLI, and MCP examples live in examples/README.md. Deploy webhook examples live in examples/deploy-webhooks.