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.
| Symptom | Likely cause | Fix |
|---|---|---|
docker: command not found / Compose missing | Docker Engine or Compose v2 not installed | Install Docker Desktop (macOS/Windows) or Engine + Compose plugin (Linux). Verify with docker compose version. |
Compose says Up but curl /health resets (RST) or hangs | App 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 disabled | DSN path used internal UUID | Copy 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/epure | Package missing or private | Wait 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 errors | Wrong DATABASE_URL or Postgres not ready | Match compose credentials; wait for postgres healthy; check docker compose logs epure |
| Connection refused on the old port | EPURE_PORT changed, or .env not picked up | Curl EPURE_PUBLIC_URL/health; recreate epure after .env edits |
| Boot refuses example DB passwords | EPURE_PUBLIC_URL is not localhost, or leftover CHANGE_ME | Run ./configure --prod or use deploy/env.production.example; set the three role passwords |
EPURE_PUBLIC_URL must be https:// | Production config without TLS origin | Set https://your-host; put Caddy/nginx in front |
EPURE_CORS_ORIGINS must list real origins | * in production | List frontend origins comma-separated |
cargo test / host tools cannot connect | Using port 5432 on host | Dev compose maps Postgres to 5433 (POSTGRES_HOST_PORT) |
| Login returns you to the form with no error | Opened http://…:8080 while the prod overlay sets a Secure cookie | Open 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 deploy | Insecure cookie on HTTPS or secure cookie on HTTP | Prod: EPURE_SESSION_SECURE=1 + TLS in front. Local HTTP: 0 |
| Strangers can register on the public URL | Public registration is still open (the default), or the clone is stale | Create your account, set EPURE_REGISTRATION=false, git pull, pin a current EPURE_IMAGE, recreate epure. Upgrades · Configuration |
| Register returns 403 | EPURE_REGISTRATION is closed | Expected for a new workspace. Sign in, or use an invitation link. Unset the variable and recreate to open signup again |
| Google button missing | GOOGLE_CLIENT_ID empty | Set Google env vars; restart epure |
| OAuth redirect error | Redirect URI mismatch | GOOGLE_REDIRECT_URI = {EPURE_PUBLIC_URL}/api/v1/auth/google/callback |
| Browser SDK CORS failure | Origin not allowed | Set EPURE_CORS_ORIGINS to your frontend origins (comma-separated) |
Ingest 401 invalid_dsn | Pre-v0.1.5 binary, or wrong/missing sentry_key | Official 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 403 | Bad or revoked DSN | Create/rotate key; update SDK; revoked keys are rejected |
| Ingest 202 but no issue | Worker lag or processing error | Wait ~500 ms; docker compose logs epure |
| Events dropped under load | Ingest cap or spike valve | Check project ingest cap (default 5000/hr); spike valve increments counters while discarding bodies |
| Webhook never arrives | Private URL / SSRF guard | Use public HTTPS; do not set EPURE_WEBHOOK_ALLOW_PRIVATE=1 in prod |
| Disk growth | Retention / partitions | Lower retention (14/30/90); TTL drops old events_YYYY_MM partitions daily |
| Stack still minified | Maps missing or wrong release | Upload 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/epureHost tools against default compose:
postgres://epure:epure@localhost:5433/epureAlso set when running outside Compose:
EPURE_INGEST_DATABASE_URL=postgres://epure_ingest:epure_ingest@localhost:5433/epureEPURE_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.txtIf 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.
| Deploy | EPURE_SESSION_SECURE | Cookie |
|---|---|---|
| Local HTTP | 0 | Epure.sid |
| HTTPS | 1 | __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/healthFAQ
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.