First triage and evidence
Before changing anything, capture the exact state:-
Identify the bisibility release or image revision and the selected Compose
files (for example
compose.yamlonly, or withcompose.worker.yamlandcompose.temporal.yaml). -
List every service and the one-shot migration result:
Add
-f compose.worker.yaml -f compose.temporal.yamlwhen scheduled checks are enabled. -
Read scoped service logs, not broad dumps:
- Never paste secrets, provider credentials, tokens, full environment dumps, or private customer data into shared logs, issues, or chat. Redact values before sharing.
App or migration startup failures
Thedb-migrations one-shot service is the startup gate. The app container
waits for it to exit successfully; Compose does not start the app until it
does.
Read the first migration failure, repair its cause, then rerun the same
selected Compose topology. Completed migration steps are skipped: Prisma
tracks applied migrations in its ledger and data migrations skip finished
rows. Do not route traffic or start worker-dependent work until required
migrations and readiness pass.
For upgrade failure and restore procedures, see
Upgrades - failure paths and
Backup and restore.
Liveness versus readiness
Readiness fails when the application database is degraded, blocking migrations
are not ready, or the resolved scheduler configuration is invalid
(
RANK_CHECK_SCHEDULER_MODE or SCHEDULER_DRIVER). Worker or Temporal service
unavailability alone does not fail readiness, because those are optional for
manual checks. Authenticated /api/v1/health surfaces the degraded worker and
Temporal state in its services block.
Never put INTERNAL_PROBE_TOKEN in a URL, query string, or log. See
Health endpoint exposure.
Sign-in redirects or callbacks
OAuth redirects and auth callbacks useSITE_URL and BETTER_AUTH_URL. Both
must match the exact public origin, including any non-default port. A
mismatch breaks redirect URIs and cookie scope.
- Compare both values with the public origin your browser shows.
- Set both to the exact origin, including the port if non-default for the scheme.
- Recreate the app container after changing environment values.
- For proxy and origin details, see OAuth and public origins and Required environment variables.
Provider and rank-check failures
Use the in-app check diagnostics and the check-run status to classify the failure:
The fallback chain tries every configured connection in order and returns
immediately if one succeeds. Only when no connection succeeds: if every
attempt was rate-limited, the check is deferred, not failed, so the keyword
stays due and a later fire retries; otherwise the chain is exhausted.
Do not invent retry intervals, prices, or vendor-specific fixes. See
Integrations for exact provider, fallback, and budget
semantics.
Worker restart loops
The worker is not the Temporal server. The worker image contains only the bisibility Temporal worker; it does not contain or start a Temporal server. Verify in order:TEMPORAL_ADDRESSpoints at a reachable Temporal server.TEMPORAL_TLSmatches the endpoint (autofor Cloud with API key,truefor self-managed TLS,falsefor plaintext on a private network).TEMPORAL_NAMESPACEexists and matches the web app setting.- Task queues (
TEMPORAL_TASK_QUEUE,TEMPORAL_ALERT_DELIVERY_TASK_QUEUE) match the web app. - The worker can reach the database and has the same release/schema as the web app.
on-failure or equivalent restart policy on custom
platforms. See
Worker startup troubleshooting
for connection setup and the Temporal owner for the full mode and environment
matrix.
Schedules not firing
Manual checks work without Temporal. Scheduled checks need both the worker and Temporal:- Confirm the worker and Temporal overlays are deployed (or the equivalent non-Compose deployment).
- Verify
SCHEDULER_DRIVER=temporalon both app and worker. - Deploy the same explicit
RANK_CHECK_SCHEDULER_MODEon app and worker. - Confirm namespace and task queues match between app and worker.
GET /api/v1/health for schedulerConfiguration status and
inspect worker logs. See
Scheduled rank checks for the
owner of the full mode and environment matrix.
Wrong client IP or shared rate-limit bucket
WhenBISIBILITY_CLIENT_IP_HEADER is unset, the app trusts no proxy header.
Anonymous API callers then share one rate-limit bucket and audit entries
record no source IP. This is the intended default.
To fix, configure the trusted header and XFF depth at your proxy. See
Trusted client IP for the exact
header selection, XFF depth rule, and spoofing warning. Do not duplicate the
proxy implementation here.
Email failures
Distinguish three failure classes:
Verify in order:
EMAIL_PROVIDERis explicitly set (resend,ses, orsmtp).- Provider-specific credentials, region, and sender are configured.
- The sender address or domain is verified with the provider.
- Provider-specific restrictions (sandbox mode, quotas, sending limits) are not blocking delivery.
Issue report checklist
Include exactly these items when reporting an issue:- Exact bisibility release or image revision.
- Deployment path and selected Compose files (or equivalent platform).
- Failing endpoint or action and timestamp.
- Status code and redacted error class/message.
- Relevant redacted service logs.
- Whether manual or scheduled work is affected.
- Whether the issue reproduces after rerunning the canonical check.
.env file, database
dumps, or customer content.