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:

sudo ./preflight.sh

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

sudo ./doctor.sh

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

SymptomLikely causeFix
Whole instance offline after an env change, api container RestartingE2B_API_KEY set with E2B_TEMPLATE_NAME emptypreflight.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 launchDNS not pointed at the host yet, or ports 80/443 blockeddoctor.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 errorRan Compose directly instead of through the scriptsUse ./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_foundThe repository was never configured as a cloud environmentExpected, 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 reachableRetry; if persistent, check doctor.sh's Redis section and docker compose logs redis.
Gateway session gets 400 Invalid model nameOffered a native model selector (e.g. default) on a gateway-only routeShould 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_disabledSSO_JIT_POLICY is at its default (disabled) and the user has no existing accountSet SSO_JIT_POLICY=create_member (or existing_user and pre-create the account), per OIDC SSO.
Invitation never arrives by emailNo 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 matchRegister exactly <API_BASE_URL>/auth/github/callback — see GitHub sign-in.

Logs

docker compose --env-file .env.runtime -f docker-compose.production.yml logs -f api

Swap api for caddy, db, litellm, or redis as needed (run from the deploy directory).

Info:

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.

On this page