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.
Boot failures
Section titled “Boot failures”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:
- Restore the correct
ENCRYPTION_MASTER_KEY— usually from your password manager or KMS. - If you’re rotating the master key intentionally, follow the encryption rotation runbook — don’t just swap the env var.
- 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.
tcp listen: address already in use
Section titled “tcp listen: address already in use”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:
# Find what owns itsudo ss -tlnp | grep :4000# Or for Docker:docker ps --format '{{.Names}} {{.Ports}}' | grep 4000
# Stop the stale containerdocker compose downdocker 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.
Postgres database "engram" does not exist
Section titled “Postgres database "engram" does not exist”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:
docker compose exec engram /app/bin/engram eval "Engram.Release.migrate"If you’re not using the bundled release runner:
docker compose exec engram mix ecto.createdocker compose exec engram mix ecto.migrateQdrant problems
Section titled “Qdrant problems”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:
- Qdrant container isn’t running / has crashed
QDRANT_URLin.envpoints to the wrong host (e.g.,localhostinside a container instead ofqdrant)- Qdrant ran out of disk and is in read-only mode
Fix:
# Confirm Qdrant is updocker compose ps qdrantcurl -sf http://qdrant:6333/healthz # from inside the engram container# orcurl -sf http://localhost:6333/healthz # from the host
# If disk-full:docker compose exec qdrant df -h /qdrant/storageIf 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_jobsWHERE 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).
Storage problems
Section titled “Storage problems”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:
# Confirm MinIO's actual credentialsdocker compose exec minio printenv MINIO_ROOT_USER MINIO_ROOT_PASSWORDUpdate .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:
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.
Embedding-provider problems
Section titled “Embedding-provider problems”Voyage API: 401 or 403
Section titled “Voyage API: 401 or 403”Symptom: Embed jobs failing with auth errors.
Cause: VOYAGE_API_KEY is missing, expired, or rate-limited.
Fix:
# Check the key works directlycurl -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:
EMBED_BACKEND=ollamaEMBED_MODEL=nomic-embed-textEMBED_DIMS=768Important: 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:11434or the host’s LAN IP, notlocalhost. - If running Ollama as a Compose service: use the service name (e.g.,
ollama:11434).
Auth / signup
Section titled “Auth / signup”Signup returns 500
Section titled “Signup returns 500”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:
docker compose exec engram printenv SECRET_KEY_BASE ENCRYPTION_MASTER_KEY DATABASE_URLSECRET_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
Authorizationheader isn’t on the strip list. - Restart Engram after any
JWT_SECRETchange; existing tokens issued under the old secret will fail.
Reporting bugs
Section titled “Reporting bugs”If your symptom isn’t covered:
- Search
engram-app/engramissues — your error message may already be tracked. - If not, open a new issue with:
- Engram version (
docker compose exec engram cat /app/lib/engram-*/ebin/engram.app | grep vsnor the image tag) docker compose psoutput- The actual error from logs (
docker compose logs engram --tail=200) - What you tried already
- Engram version (
Do not include ENCRYPTION_MASTER_KEY, SECRET_KEY_BASE, JWT_SECRET, or any other secret in your issue. Redact them before pasting log lines.