Verify Your Install

Run this after a first install, an upgrade, or a restore. These checks cover the core deployment; also verify any optional features you enabled, such as SSO, SCIM, data drains, and remote sandboxes.

sim-setup doctor

Run this first. It is the fastest signal you have, and it catches most bad installs before you open a browser.

npx sim-setup doctor

Run it from the directory holding your .env and Compose file, or from a source checkout. It reports five groups:

GroupWhat it checks
Env filesEvery .env the layout requires exists. --fix seeds a missing one from the example plus the values already in apps/sim/.env
SchemaRequired keys are present, are not still the example placeholder, are valid URLs where they should be, and have a usable shape — ENCRYPTION_KEY is checked as exactly 64 hex characters, the rest as a minimum length. Optional secrets are not shape-checked, so a malformed API_ENCRYPTION_KEY passes here and fails later at encrypt time. --fix generates missing or placeholder secrets
ConsistencyThe same key agrees across files — BETTER_AUTH_SECRET between the app and realtime, and DATABASE_URL between the app and packages/db (a mismatch there means migrations ran against a different database). --fix mirrors the apps/sim/.env value
CoherenceConfiguration that is individually valid but jointly wrong: a feature flag whose dependency is unset, a storage provider that is half-configured, and a server flag whose NEXT_PUBLIC_ twin disagrees with it
LivePostgres is reachable, pgvector is available, migrations are applied — on a standalone npx sim-setup install the published package carries no migration journal, so it can confirm what has been applied but not that it is current — Redis answers, and the running services respond on their ports. Doctor reads env files, so on the production Compose layout — where those values live inside the containers rather than in .env — the Postgres, migration, and Redis probes are skipped and only the HTTP health checks run

The check worth knowing about before you hit it: if NEXT_PUBLIC_APP_URL points at sim.ai or any subdomain of it, Sim treats the deployment as Sim's own hosted platform (isHosted=true) and disables self-host overrides. Doctor flags this. Use your own domain.

Two flags matter:

  • --fix applies the safe repairs above and re-runs the checks. It never touches anything it cannot repair deterministically.
  • --json prints the findings as structured JSON instead of the report, so CI can gate on it. The command exits 1 when anything failed.

The full command list is in the Docker guide. Doctor reads env files, so it covers Compose installs and source checkouts — it is not a Kubernetes tool. On Helm, use the infrastructure checks below.

Checklist

#Do thisProvesIf it fails
1Open your Sim URL and create an accountApp, database, TLS, migrationskubectl logs deploy/sim-app — and check the migrations init container
2Sign out and sign back inSession handling, BETTER_AUTH_SECRET, BETTER_AUTH_URLURLs must match your real origin exactly
3Open a workflow and drag two blocks into the editorRealtime websocket connectionBrowser console for socket errors; see Networking
4Open the same workflow in a second browser window and editCross-replica collaborationWith >1 replica this needs Redis
5Paste a model API key in settings and run a two-block workflowExecution engine, credential encryption, outbound networkApp logs; check ENCRYPTION_KEY is set and outbound egress is allowed
6Upload a small file in FilesFile storage end to endWith object storage configured: presigned URL + bucket CORS. On local disk: the upload proxies through the app
7Upload a file larger than 50 MBMultipart upload path (object storage only)Check app logs for provider part-listing or completion errors
8Create a knowledge base and upload a PDFDocument parsing, embeddings, pgvectorNeeds an embedding provider — see below
9Invite a teammate from workspace settingsEmail deliveryApp logs for the mailer; see Email
10Connect an integration accountOAuth configurationRedirect URI mismatch → see Integrations & OAuth
11Create a workflow with a Schedule trigger set to every minute, deploy it, wait 2 minutesBackground jobsCheck the scheduler's logs — see Background Jobs
12Trigger a workflow via the API with an API keyPublic API and API-key authCheck the key was created successfully in settings

Step 11 is the one most people skip and most often discover broken weeks later. Scheduled workflows and every polling trigger depend on the scheduler, and a wrong or missing CRON_SECRET makes it fail silently from the app's side.

Infrastructure checks

After the checklist, confirm the deployment itself is healthy.

# Everything running?
kubectl get pods -n simstudio

# Migrations completed
kubectl logs -n simstudio deploy/sim-app -c migrations --tail=50

# Health endpoints
curl -fsS https://sim.yourdomain.com/api/health

# Background jobs scheduled
kubectl get cronjobs -n simstudio

# Redis configured (multi-replica deployments) — confirms the variable is set,
# not that Redis answers. Checklist steps 3 and 4 above are the real reachability test.
kubectl exec -n simstudio deploy/sim-app -- printenv REDIS_URL
# Docker Compose
docker compose -f docker-compose.prod.yml ps
docker compose -f docker-compose.prod.yml logs migrations
curl -fsS http://localhost:3000/api/health

All six should be present on Compose: simstudio, realtime, db, redis, cron, and a completed migrations.

Reading the failures

Step 1 fails — app will not load. Almost always migrations or database connectivity. Check the migrations init container first; a failed migration deliberately blocks the rollout.

Step 2 fails — login loops or rejects. NEXT_PUBLIC_APP_URL or BETTER_AUTH_URL does not match the origin you are browsing. Both must be the exact public URL, with scheme and no trailing slash.

Step 3 fails — no live updates. The reverse proxy is not passing websocket upgrades, or /socket.io is not routed to the realtime service. If realtime is on a separate hostname, NEXT_PUBLIC_SOCKET_URL must point at it and realtime's ALLOWED_ORIGINS must include the app origin.

Step 4 fails — edits do not sync between windows. With more than one replica, this is Redis. Confirm REDIS_URL is present on both pods.

Step 5 fails — execution errors. Check outbound connectivity to the model provider, then the app logs. If the error is about decrypting a credential, ENCRYPTION_KEY differs from the one that encrypted it.

Step 6 or 7 fails. With object storage configured, a CORS error in the browser console means the bucket policy does not allow your Sim origin or the signed upload headers. If step 7 fails only during completion, check the app logs and verify the server identity can list multipart parts (for S3, s3:ListMultipartUploadParts). On local-disk storage there is no CORS involved — uploads proxy through the app, so look at the app logs and the proxy body-size limit instead.

Step 8 fails — knowledge base upload errors. Knowledge bases need an embedding provider: OpenAI, Azure OpenAI, or Gemini with an API key, or a model on your own Ollama via KB_EMBEDDING_MODEL=ollama/<model> and OLLAMA_URL. If one is configured, check pgvector is installed on the database.

Step 9 fails — no email arrives. With no provider configured the mailer no-ops: it records the recipient, subject, and sender at info and reports success, never the message body. Raise LOG_LEVEL to INFO to see that line — the variable is uppercase-only, and at the production default of ERROR nothing is logged at all.

Step 11 fails — schedule never fires. Read the scheduler's logs (docker compose -f docker-compose.prod.yml logs cron, or kubectl get cronjobs -n simstudio). A 401 there means the app and the scheduler disagree on CRON_SECRET.

After an upgrade

Re-run steps 1, 3, 5, 6, and 11 at minimum. Those cover the app, realtime, execution, storage, and background jobs — the five things a bad upgrade breaks.

After a restore

Run the whole list, and pay special attention to step 5 with an OAuth-backed integration. That is what proves ENCRYPTION_KEY matches the backup. An app that loads and logs in but cannot decrypt credentials looks healthy right up until someone runs a real workflow.