Skip to content

Troubleshooting

If your self-host instance isn’t behaving, work through this list before opening an issue. The failures below are the ones operators actually hit; each has a known root cause and fix.

If your symptom isn’t here, see Reporting bugs at the bottom.

BootCanary: master key does not match existing user DEKs

Section titled “BootCanary: master key does not match existing user DEKs”

Symptom: Engram refuses to start. Log shows a BootCanary error mentioning the master key.

Cause: The ENCRYPTION_MASTER_KEY in your env doesn’t match the one used to encrypt the per-user DEKs in Postgres. Engram refuses to boot rather than silently produce garbled decryptions.

Fix:

  1. Restore the correct ENCRYPTION_MASTER_KEY — usually from your password manager or KMS.
  2. If you’re rotating the master key intentionally, follow the encryption rotation runbook — don’t just swap the env var.
  3. If the key is genuinely lost, data encrypted under it cannot be recovered. Restore from a backup taken before the rotation, or wipe + start over.

Symptom: Phoenix won’t bind to port 4000 (or whatever you set). Container exits immediately.

Cause: Another process owns the port — often a previous Engram container that didn’t shut down cleanly, or another service.

Fix:

Terminal window
# Find what owns it
sudo ss -tlnp | grep :4000
# Or for Docker:
docker ps --format '{{.Names}} {{.Ports}}' | grep 4000
# Stop the stale container
docker compose down
docker rm -f $(docker ps -aq --filter "name=engram")

If a non-Engram process owns the port, change PORT in your .env to a free port and update your reverse proxy.

Symptom: First boot fails with a database-not-found error.

Cause: You skipped or interrupted the mix ecto.setup step (creates DB + runs migrations).

Fix:

Terminal window
docker compose exec engram /app/bin/engram eval "Engram.Release.migrate"

If you’re not using the bundled release runner:

Terminal window
docker compose exec engram mix ecto.create
docker compose exec engram mix ecto.migrate

Qdrant unreachable or repeated econnrefused in logs

Section titled “Qdrant unreachable or repeated econnrefused in logs”

Symptom: Notes save fine but search returns empty results. Embed jobs in Oban dashboard show repeated failures.

Cause:

  1. Qdrant container isn’t running / has crashed
  2. QDRANT_URL in .env points to the wrong host (e.g., localhost inside a container instead of qdrant)
  3. Qdrant ran out of disk and is in read-only mode

Fix:

Terminal window
# Confirm Qdrant is up
docker compose ps qdrant
curl -sf http://qdrant:6333/healthz # from inside the engram container
# or
curl -sf http://localhost:6333/healthz # from the host
# If disk-full:
docker compose exec qdrant df -h /qdrant/storage

If QDRANT_URL is localhost:6333 but Engram is in a container, change it to http://qdrant:6333 (Docker Compose service name) and restart.

Search returns results but they’re missing recent notes

Section titled “Search returns results but they’re missing recent notes”

Symptom: Search works for old notes but new ones don’t appear.

Cause: The Oban embedding queue is backed up, paused, or the worker is crashing.

Fix: Check the Oban dashboard at /dev/oban (only enabled in dev — for prod, query directly):

SELECT state, worker, count(*)
FROM oban_jobs
WHERE worker LIKE '%Embed%'
GROUP BY 1, 2;

Look for jobs stuck in retryable with a recurring error. The error column will name the underlying cause (Voyage / Ollama outage, Qdrant unreachable, embedding-dim mismatch).

MinIO: signature does not match or attachments 403

Section titled “MinIO: signature does not match or attachments 403”

Symptom: Attachments fail to upload or download with a signature-mismatch error.

Cause: STORAGE_ACCESS_KEY / STORAGE_SECRET_KEY in Engram’s env don’t match the MinIO root credentials.

Fix:

Terminal window
# Confirm MinIO's actual credentials
docker compose exec minio printenv MINIO_ROOT_USER MINIO_ROOT_PASSWORD

Update .env so STORAGE_ACCESS_KEY and STORAGE_SECRET_KEY match. Note: the upstream FastRaid MinIO template ships with a placeholder password literal __MINIO_PASS__; if you copied that template without substituting, your real password is that string. Replace it with a real value.

STORAGE_BACKEND=s3 but attachments still go to Postgres

Section titled “STORAGE_BACKEND=s3 but attachments still go to Postgres”

Symptom: Database grows fast, attachments aren’t appearing in MinIO.

Cause: STORAGE_BACKEND isn’t actually set in the env Engram sees. runtime.exs defaults to database when the env var is missing.

Fix:

Terminal window
docker compose exec engram printenv STORAGE_BACKEND
# Should print "s3". If empty:

Set it explicitly in .env and docker-compose.yml — relying on the default loses you the S3 path silently.

Symptom: Embed jobs failing with auth errors.

Cause: VOYAGE_API_KEY is missing, expired, or rate-limited.

Fix:

Terminal window
# Check the key works directly
curl -sf https://api.voyageai.com/v1/embeddings \
-H "Authorization: Bearer $VOYAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"test","model":"voyage-4-large"}' | jq .

If you’re self-hosting and don’t want a Voyage account, switch to Ollama:

Terminal window
EMBED_BACKEND=ollama
EMBED_MODEL=nomic-embed-text
EMBED_DIMS=768

Important: switching dimensions on an existing instance requires a Qdrant re-index — the old vectors are incompatible. See the upgrade path for the procedure.

Ollama: connection refused on localhost:11434

Section titled “Ollama: connection refused on localhost:11434”

Symptom: Embed jobs fail with econnrefused to Ollama.

Cause: Ollama isn’t running, or Engram is in a container and localhost resolves inside the container (where Ollama isn’t).

Fix:

  • If running Ollama on the host: use host.docker.internal:11434 or the host’s LAN IP, not localhost.
  • If running Ollama as a Compose service: use the service name (e.g., ollama:11434).

Symptom: Trying to create a user via API or web returns a server error.

Cause: Most often SECRET_KEY_BASE or ENCRYPTION_MASTER_KEY isn’t set, or DATABASE_URL is wrong.

Fix:

Terminal window
docker compose exec engram printenv SECRET_KEY_BASE ENCRYPTION_MASTER_KEY DATABASE_URL

SECRET_KEY_BASE should be ≥ 64 chars (generate with openssl rand -base64 48). ENCRYPTION_MASTER_KEY should be 32 bytes base64’d (openssl rand -base64 32). Both are required for boot.

Login works but every authenticated request returns 401

Section titled “Login works but every authenticated request returns 401”

Symptom: The login endpoint returns a token, but using that token immediately returns 401.

Cause: Your reverse proxy is stripping the Authorization header, or your JWT secret rotated between issuing and verifying.

Fix:

  • nginx: check proxy_pass_header Authorization; is set.
  • Cloudflare or NPM: confirm the Authorization header isn’t on the strip list.
  • Restart Engram after any JWT_SECRET change; existing tokens issued under the old secret will fail.

If your symptom isn’t covered:

  1. Search engram-app/engram issues — your error message may already be tracked.
  2. If not, open a new issue with:
    • Engram version (docker compose exec engram cat /app/lib/engram-*/ebin/engram.app | grep vsn or the image tag)
    • docker compose ps output
    • The actual error from logs (docker compose logs engram --tail=200)
    • What you tried already

Do not include ENCRYPTION_MASTER_KEY, SECRET_KEY_BASE, JWT_SECRET, or any other secret in your issue. Redact them before pasting log lines.