Epure
Self-hostingTroubleshooting

Troubleshooting

Login form accepts the password, then shows itself again

You opened Epure on HTTP, usually http://YOUR_SERVER:8080. Production sets a Secure cookie, and the browser drops it. Open https://your-hostname on port 443. Start Caddy with docker compose -f docker-compose.yml -f deploy/docker-compose.prod.yml --profile tls up -d after ./configure --prod. A raw IP will not get a certificate.

What usually fails on a self-host?

A wrong database URL, a session cookie Secure mismatch, a missing CORS origin, or a revoked DSN key. Start with the matrix.

SymptomLikely causeFix
docker: command not found / Compose missingDocker Engine or Compose v2 not installedInstall Docker Desktop (macOS/Windows) or Engine + Compose plugin (Linux). Verify with docker compose version.
Compose says Up but curl /health resets (RST) or hangsApp cannot reach Postgres (host firewall, broken bridge, custom network)docker compose ps — both services healthy? docker compose logs epure for migrate/DB errors. From the host: docker compose exec epure wget -qO- http://127.0.0.1:8080/health (bypasses port publish). If that works but localhost fails, check port mapping and local firewall. docker compose exec epure pg_isready -h postgres -p 5432. Recreate: docker compose down && docker compose up -d.
@sentry/node logs Invalid projectId / transport disabledDSN path used internal UUIDCopy the DSN from Settings → SDK connection — the path segment is a numeric project id, not the UUID in /p/:uuid/… URLs. UUID paths still work for curl; official SDKs need the numeric DSN.
up cannot pull ghcr.io/epure-sh/epurePackage missing or privateWait for a tagged Image workflow; set the GHCR package to public. Contributors: docker compose -f docker-compose.yml -f deploy/docker-compose.build.yml up --build
App exits on start / migrate errorsWrong DATABASE_URL or Postgres not readyMatch compose credentials; wait for postgres healthy; check docker compose logs epure
Connection refused on the old portEPURE_PORT changed, or .env not picked upCurl EPURE_PUBLIC_URL/health; recreate epure after .env edits
Boot refuses example DB passwordsEPURE_PUBLIC_URL is not localhost, or leftover CHANGE_MERun ./configure --prod or use deploy/env.production.example; set the three role passwords
EPURE_PUBLIC_URL must be https://Production config without TLS originSet https://your-host; put Caddy/nginx in front
EPURE_CORS_ORIGINS must list real origins* in productionList frontend origins comma-separated
cargo test / host tools cannot connectUsing port 5432 on hostDev compose maps Postgres to 5433 (POSTGRES_HOST_PORT)
Login returns you to the form with no errorOpened http://…:8080 while the prod overlay sets a Secure cookieOpen the HTTPS URL. ./configure --prod plus --profile tls starts Caddy. The login page states this when the cookie cannot be stored
Login loops after HTTPS deployInsecure cookie on HTTPS or secure cookie on HTTPProd: EPURE_SESSION_SECURE=1 + TLS in front. Local HTTP: 0
Strangers can register on the public URLPublic registration is still open (the default), or the clone is staleCreate your account, set EPURE_REGISTRATION=false, git pull, pin a current EPURE_IMAGE, recreate epure. Upgrades · Configuration
Register returns 403EPURE_REGISTRATION is closedExpected for a new workspace. Sign in, or use an invitation link. Unset the variable and recreate to open signup again
Google button missingGOOGLE_CLIENT_ID emptySet Google env vars; restart epure
OAuth redirect errorRedirect URI mismatchGOOGLE_REDIRECT_URI = {EPURE_PUBLIC_URL}/api/v1/auth/google/callback
Browser SDK CORS failureOrigin not allowedSet EPURE_CORS_ORIGINS to your frontend origins (comma-separated)
Ingest 401 invalid_dsnPre-v0.1.5 binary, or wrong/missing sentry_keyOfficial SDKs send public-key-only auth. Pin ghcr.io/epure-sh/epure:v0.1.7 (v0.1.5+), git pull, recreate. Copy the DSN from Settings → SDK connection. EPURE_INGEST_PASSWORD is the Postgres ingest role password — not an SDK field
Ingest 403Bad or revoked DSNCreate/rotate key; update SDK; revoked keys are rejected
Ingest 202 but no issueWorker lag or processing errorWait ~500 ms; docker compose logs epure
Events dropped under loadIngest cap or spike valveCheck project ingest cap (default 5000/hr); spike valve increments counters while discarding bodies
Webhook never arrivesPrivate URL / SSRF guardUse public HTTPS; do not set EPURE_WEBHOOK_ALLOW_PRIVATE=1 in prod
Disk growthRetention / partitionsLower retention (14/30/90); TTL drops old events_YYYY_MM partitions daily
Stack still minifiedMaps missing or wrong releaseUpload JS/TS maps to the matching release version (Releases)

Which database URL should I use?

Inside the epure container, Postgres is postgres:5432. On the host against default compose, use 5433 (POSTGRES_HOST_PORT in .env).

Compose (inside the epure container):

postgres://epure:epure@postgres:5432/epure

Host tools against default compose:

postgres://epure:epure@localhost:5433/epure

Also set when running outside Compose:

  • EPURE_INGEST_DATABASE_URL=postgres://epure_ingest:epure_ingest@localhost:5433/epure
  • EPURE_APP_DATABASE_URL=postgres://epure_app:epure_app@localhost:5433/epure

The prod overlay publishes no Postgres port; reach it with docker compose exec postgres psql -U epure -d epure.

When do migrations run?

Migrations apply on Epure startup, never as a manual job. Failures show up in docker compose logs epure.

Fix connectivity or credentials, then recreate the app container. After a failed upgrade migration, restore the pre-upgrade dump before retrying a new image.

How do I test DSN ingest?

POST envelope or store with X-Sentry-Auth using sentry_key only. Expect 202 and { "id": "<uuid>" }; the body cap is 2 MB.

Interactive builder: Quickstart · Try ingest.

curl -sS -w "\nHTTP %{http_code}\n" \
  -X POST "http://localhost:8080/api/${PROJECT_ID}/envelope/" \
  -H "Content-Type: application/x-sentry-envelope" \
  -H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=${PUBLIC}" \
  --data-binary @fixtures/sentry/browser/envelope.txt

If you get 401 invalid_dsn with a correct public key, upgrade to v0.1.5+ — older binaries required a secret that modern SDKs no longer send.

How do I debug CORS?

Browser SDKs need a successful OPTIONS preflight on envelope and store. Expect 200 or 204 with Access-Control-Allow-Origin.

curl -sS -D - -o /dev/null -X OPTIONS \
  "http://localhost:8080/api/${PROJECT_ID}/envelope/" \
  -H "Origin: http://localhost:5173" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: X-Sentry-Auth, Content-Type"

Why does my session drop?

Login succeeds, then /api/v1/* acts logged out. The browser is rejecting the cookie over a Secure flag mismatch.

DeployEPURE_SESSION_SECURECookie
Local HTTP0Epure.sid
HTTPS1__Host-epure.sid (Secure only)

How do I get more signal?

Follow the logs, raise the log level, then check container state and health. Recreate epure after any env change.

docker compose logs -f Epure
RUST_LOG=debug   # temporary: set in compose/env, then recreate
docker compose ps
curl -sS http://localhost:8080/health
Why does the app exit on migrate?

Wrong DATABASE_URL, or Postgres is not ready. Wait for postgres to report healthy and read docker compose logs epure. Host tools must use port 5433, and epure needs a recreate after credentials change. Copy URLs from Configuration.

Why is ingest 401 invalid_dsn?

Wrong or missing sentry_key, or a binary older than v0.1.5. Official Sentry SDKs authenticate with the public key only — they do not send sentry_secret. Pin ghcr.io/epure-sh/epure:v0.1.7 (v0.1.5+), git pull, recreate epure, and paste the DSN from Settings → SDK connection. Do not put EPURE_INGEST_PASSWORD into the SDK; that env var is the Postgres epure_ingest role password. Symptom guide: Invalid DSN. Authentication · Upgrades.

Why is ingest 403?

The DSN public key is missing, wrong, or revoked, or the URL project id does not match the key. Copy a new key from Settings → SDK connection. Revoked keys return dsn_revoked and mismatched project ids return project_mismatch. The hourly cap returns ingest_cap_exceeded: Verify.

Why 202 with no issue?

Accept is non-blocking, so wait about a second. Match the environment chip to the SDK tag and confirm the project that owns the DSN. Then read docker compose logs epure. Spike-valve floods still return 202 while dropping bodies: Verify. If the list stays empty: Events not showing.

Why does login loop after HTTPS?

The production overlay sets a Secure cookie. On HTTP the browser drops it and the app sends you back to /login with no error. Open the HTTPS URL. --profile tls starts Caddy for EPURE_SITE_ADDRESS. Local HTTP needs EPURE_SESSION_SECURE=0. Google OAuth also needs GOOGLE_REDIRECT_URI equal to {EPURE_PUBLIC_URL}/api/v1/auth/google/callback. Restart epure after env changes.

Why are events dropped under load?

The hourly project cap (default 5000) returns 403 ingest_cap_exceeded. The spike valve (~100/min/fingerprint) still returns 202 and bumps counters while dropping bodies. Raise the cap in project settings, or slow the flood. Codes: API errors.