Edge ingest on Cloudflare
Deploy an optional edge ingest tier when you operate Lookout at high volume. Customers keep the same project API key and POST /api/ingest contract; you give them an edge hostname for their DSN instead of the main app URL.
This guide is for Lookout operators (your deployment .env and Cloudflare account). Customer SDKs do not need extra env vars.
How it works
Customer app → Cloudflare Worker (/api/ingest, project API key)
↓ KV validate, 202 immediately
Cloudflare Queue
↓ Bearer LOOKOUT_INGEST_EDGE_TOKEN
Lookout backend POST /api/ingest/edge → queue → MySQL
| Hop | Authentication |
|---|---|
| Customer → Worker | Project API key (X-Api-Key / Bearer) validated against Workers KV |
| Worker → Lookout | Shared secret LOOKOUT_INGEST_EDGE_TOKEN as Authorization: Bearer … |
The edge token is not a project API key. Generate it once and store the same value on Lookout and in the Worker secret.
Prerequisites
- A running Lookout deployment with Redis, queue workers (Horizon or
queue:work), and the same database the UI uses. - A Cloudflare account with Workers, Workers KV, and Queues enabled.
- Wrangler CLI (
npm install -g wrangleror use the package-local devDependency). - Worker source in this repo:
packages/lookout-ingest-edge/.
Step 1 — Generate LOOKOUT_INGEST_EDGE_TOKEN
On your machine:
openssl rand -hex 32
Copy the output. You will set this identical string in two places:
- Lookout server
.env→LOOKOUT_INGEST_EDGE_TOKEN=… - Cloudflare Worker secret →
wrangler secret put LOOKOUT_INGEST_EDGE_TOKEN
Treat it like an internal service password. Rotate by updating both sides and redeploying the worker.
Step 2 — Configure Lookout (backend)
Add to the Lookout app .env (UI + ingest backend, or ingest-only hosts that receive forwarded events):
LOOKOUT_INGEST_EDGE_ENABLED=true
LOOKOUT_INGEST_EDGE_TOKEN=paste-the-openssl-output-here
LOOKOUT_INGEST_EDGE_BACKEND_URL=https://your-lookout-app.example.com
CLOUDFLARE_ACCOUNT_ID=your-account-id
CLOUDFLARE_KV_NAMESPACE_ID=your-kv-namespace-id
CLOUDFLARE_API_TOKEN=token-with-kv-edit-and-workers-read
| Variable | Purpose |
|---|---|
LOOKOUT_INGEST_EDGE_ENABLED |
Enables KV sync schedule and high-volume ingest optimizations |
LOOKOUT_INGEST_EDGE_TOKEN |
Must match the Worker secret; secures POST /api/ingest/edge |
LOOKOUT_INGEST_EDGE_BACKEND_URL |
Public base URL of Lookout (no trailing slash); Worker consumer posts here |
CLOUDFLARE_* |
Used by php artisan lookout:sync-ingest-edge-keys to push project API keys into KV |
Create a Cloudflare API token with Workers KV Storage → Edit on the ingest namespace and account read access. Restrict to the minimum scopes you need.
Reload config (php artisan config:clear or redeploy). The internal forward route is:
POST https://uselookout-2ku5bv.on-dply.com/api/ingest/edge
It returns 503 until LOOKOUT_INGEST_EDGE_TOKEN is set.
Step 3 — Create KV namespace and Queue (Cloudflare)
From packages/lookout-ingest-edge/:
cd packages/lookout-ingest-edge
npm install
npx wrangler login
npx wrangler kv namespace create INGEST_KEYS
npx wrangler queues create lookout-ingest
Note the namespace id from the KV create output and paste it into:
- Lookout
.env→CLOUDFLARE_KV_NAMESPACE_ID wrangler.toml→[[kv_namespaces]]→id = "…"
Ensure wrangler.toml queue name matches (lookout-ingest).
Step 4 — Configure wrangler.toml
Edit packages/lookout-ingest-edge/wrangler.toml:
[vars]
LOOKOUT_BACKEND_URL = "https://your-lookout-app.example.com"
Set max_concurrency under [[queues.consumers]] to match how much load your Lookout backend can absorb (start with 25, tune with Horizon).
Step 5 — Set Worker secrets and deploy
Still in packages/lookout-ingest-edge/:
npx wrangler secret put LOOKOUT_INGEST_EDGE_TOKEN
# paste the same value as in Lookout .env
npm run deploy
Wrangler prints the *.workers.dev URL and any custom domains from wrangler.toml.
Custom domain (recommended)
If your Lookout marketing/app zone is on the same Cloudflare account as the Worker, add a custom domain in packages/lookout-ingest-edge/wrangler.toml:
workers_dev = true
[[routes]]
pattern = "ingest.uselookout.app"
custom_domain = true
Run npm run deploy again. Cloudflare creates the DNS record and TLS certificate automatically — no manual CNAME required.
This deployment: https://ingest.uselookout.app (plus the *.workers.dev URL for testing).
To use a different hostname, change pattern and redeploy. The zone (e.g. uselookout.app) must be active on the account you wrangler login with.
Dashboard alternative: Workers & Pages → lookout-ingest-edge → Settings → Domains & Routes → Add → Custom Domain.
The worker only handles POST /api/ingest (same path as today).
Step 6 — Sync project API keys to KV
On the Lookout server:
php artisan lookout:sync-ingest-edge-keys
When LOOKOUT_INGEST_EDGE_ENABLED=true, this also runs every five minutes via the scheduler. Keys refresh when a project API key is regenerated.
Verify a key exists (replace values):
npx wrangler kv key get --binding=INGEST_KEYS "PROJECT_API_KEY_HERE"
You should see JSON with project_id, billing_ok, etc.
Step 7 — Point customer DSNs at the edge
Customers change only the host in their DSN / ingest URL:
# Before (main app)
LOOKOUT_DSN=https://PROJECT_API_KEY@lookout.example.com
# After (edge — custom domain)
LOOKOUT_DSN=https://PROJECT_API_KEY@ingest.uselookout.app
# Or workers.dev during testing
# LOOKOUT_DSN=https://PROJECT_API_KEY@lookout-ingest-edge.flat-thunder-531a.workers.dev
Same API key, same payload, same POST /api/ingest. No new SDK env vars.
Step 8 — Verify end-to-end
-
Edge accept — from any machine:
curl -sS -o /dev/null -w "%{http_code}\n" \ -X POST "https://ingest.uselookout.app/api/ingest" \ -H "X-Api-Key: YOUR_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"message":"edge smoke test","exception_class":"RuntimeException"}'Expect 202.
-
Lookout UI — open the project; the event should appear after the queue consumer forwards and
StoreIngestedEventruns. -
Bad edge token — if the Worker secret and Lookout
.envtoken differ, the consumer logs forward failures and events will not persist.
Related operator options
| Mode | Env | Doc |
|---|---|---|
| Dedicated ingest app servers | LOOKOUT_INGEST_ONLY=true |
Ingest satellite README |
| Force deferred DB side-effects without edge | LOOKOUT_INGEST_MINIMAL_IMPACT=true |
Ingest API — high volume |
Edge ingest and ingest-only mode automatically defer non-critical writes (api_key_last_used_at, release markers) to a Redis flush every 30 seconds.
Troubleshooting
| Symptom | Check |
|---|---|
| Edge returns 401 | API key missing from KV — run lookout:sync-ingest-edge-keys |
| Edge returns 402 | Organization billing delinquent in synced KV metadata |
| Edge returns 403 | Client IP blocked by org ingest allowlist |
Lookout 503 on /api/ingest/edge |
LOOKOUT_INGEST_EDGE_TOKEN unset on Lookout |
| Events accepted at edge but not in UI | Queue consumer errors — Cloudflare dashboard → Queues; Worker logs; Horizon processing StoreIngestedEvent |
| Stale billing / IP rules | Wait for sync schedule or run sync manually after org settings change |
Package reference
Monorepo path: packages/lookout-ingest-edge/ (Worker source, wrangler.toml, npm run deploy).