Troubleshoot
Diagnose a self-hosted deployment with doctor.sh, preflight.sh, and known failure signatures.
Two scripts in the deploy directory (/opt/proliferate/server/deploy on a
guided install) cover the vast majority of self-hosting problems: preflight.sh
catches a dangerous configuration before it touches a running instance, and
doctor.sh reports the current state of everything, redacting every secret
value along the way.
preflight.sh: catches bad config before it boots
bootstrap.sh and update.sh both run preflight.sh on the resolved
.env.runtime, right after secrets are generated and before any docker compose lifecycle command — so a dangerous partial config fails here, leaving
a previously healthy instance untouched instead of crash-looping it. Run it by
hand any time:
It checks, in order: SITE_ADDRESS is set (or the sslip fallback is on) and
isn't still the placeholder value; the E2B_API_KEY / E2B_TEMPLATE_NAME
pair is complete or both empty (the crash-loop trap — see
Cloud sandboxes); the agent
gateway's master key, Postgres password, public URL, and at least one
provider key are all present when AGENT_GATEWAY_ENABLED=true; the bundled
Redis URL still points at the bundled service when cloud workspaces are
configured; OIDC SSO has a client id, secret (unless a public client), and an
endpoint source when SSO_ENABLED=true, plus a warning if the default
SSO_JIT_POLICY=disabled combined with an empty ADMIN_EMAILS would lock
every SSO user out; GitHub OAuth and the GitHub App each have complete
credential pairs; the cloud-sandbox runtime binaries are reachable; and any
env key that isn't in .env.production.example or the documented advanced
overrides (likely a typo).
Exit code 0 means no errors (warnings may still have printed); 1 means an
error was found and the caller must not replace a healthy stack.
PROLIFERATE_PREFLIGHT_STRICT=1 treats warnings as errors too, for CI-style
gating.
doctor.sh: what's actually running
Reports, section by section: host (OS, architecture, Docker/Compose,
disk, ports 80/443); configuration (the same shape checks as preflight);
compose services and enabled profiles; local and public /health + /meta,
DNS resolution, and TLS certificate expiry; version compatibility against the
newest published server-v* release; Redis (only checked when cloud
workspaces are configured); the agent gateway (litellm running + healthy,
the public /llm endpoint reachable, at least one provider key set — only
checked when enabled); deployment SSO (only checked when enabled, including
the same first-user-lockout warning as preflight); and the shape — never the
value — of GitHub OAuth, the GitHub App, E2B, and Resend configuration. It
ends with a pass/warn/fail summary.
Every secret is reported as set (N chars) or not set, never the actual
value, so it's safe to paste doctor output into a support request.
Known failure signatures
| Symptom | Likely cause | Fix |
|---|---|---|
Whole instance offline after an env change, api container Restarting | E2B_API_KEY set with E2B_TEMPLATE_NAME empty | preflight.sh should have caught this before you ran update.sh; set the missing var, or clear E2B_API_KEY, and rerun. |
Public /health never responds after launch | DNS not pointed at the host yet, or ports 80/443 blocked | doctor.sh's Endpoints section reports DNS resolution and port checks. Let's Encrypt needs both ports reachable from the internet. |
Raw docker compose pull/up fails with a missing env file error | Ran Compose directly instead of through the scripts | Use ./update.sh (or --env-file .env.runtime, since Compose's default .env doesn't exist in this bundle). |
Cloud workspace creation returns cloud_repo_environment_not_found | The repository was never configured as a cloud environment | Expected, not a bug: configure the repo (PUT /v1/cloud/repositories/{owner}/{repo}/environment) before creating a workspace. See Cloud sandboxes. |
Cloud workspace creation returns cloud_materialization_busy (503) | The Redis lock is contended, or Redis isn't reachable | Retry; if persistent, check doctor.sh's Redis section and docker compose logs redis. |
Gateway session gets 400 Invalid model name | Offered a native model selector (e.g. default) on a gateway-only route | Should not happen after #1106 — file it as a bug rather than a config issue; readiness and launch are meant to agree on the credential route. |
SSO sign-in fails with sso_jit_disabled | SSO_JIT_POLICY is at its default (disabled) and the user has no existing account | Set SSO_JIT_POLICY=create_member (or existing_user and pre-create the account), per OIDC SSO. |
| Invitation never arrives by email | No RESEND_API_KEY set (the self-hosted default) | Expected: use the Copy invite link action in the members list; delivery shows as skipped, not failed. |
| GitHub sign-in shows "redirect_uri mismatch" | OAuth app's callback URL doesn't match | Register exactly <API_BASE_URL>/auth/github/callback — see GitHub sign-in. |
Logs
Swap api for caddy, db, litellm, or redis as needed (run from the
deploy directory).
Neither script requires network egress beyond what it's already checking
(the public health probe and the release-version check); both run entirely
against the local .env.runtime/.env.static otherwise.