Skip to main content
All configuration lives in config/nightowl.php after running php artisan nightowl:install. Most values are driven by environment variables — the defaults are tuned for a single-instance deployment doing up to a few thousand requests per second.

Database connection

Use the same credentials you entered when creating the app in the dashboard — the hosted UI uses them to read the telemetry your agent wrote.
Managed Postgres (Supabase, Neon, RDS): point the agent at the direct or session connection on port 5432, not a transaction-mode pooler (Supabase’s 6543). The agent holds a long-lived drain connection and relies on session-level settings (synchronous_commit) that transaction-mode poolers don’t preserve, so a session-scoped connection is the right fit. Set NIGHTOWL_DB_SSLMODE=require, and use the provider’s existing database (postgres on Supabase, neondb on Neon) — NightOwl creates the tables, not the database.

Sharing one database across environments

NightOwl stamps an environment column (your APP_ENV) on every row, so you can point several app environments (local, staging, production) at one NightOwl database and filter them apart in the dashboard. The environments are separated only by that column; the nightowl_* tables themselves are shared. (Unrelated to table partitioning — see Data management for that.) nightowl:install and nightowl:migrate track their migration history inside the NightOwl database, so they’re idempotent across every environment that shares it. Run php artisan nightowl:migrate as part of each deploy:
  • the first environment to deploy creates the tables;
  • the rest are no-ops;
  • when a package upgrade adds a migration, it applies on whichever environment deploys first.
No “owner” environment, no flags. If a database already has the nightowl_* tables but no NightOwl migration history (for example it was created by an older version, or by your app’s php artisan migrate), the command adopts the existing schema as a baseline instead of trying to recreate it — so you never hit relation "nightowl_requests" already exists.
By default NightOwl’s migrations are not bundled into your app’s php artisan migrate. Manage the schema with nightowl:migrate (or nightowl:install). See NIGHTOWL_RUN_MIGRATIONS below if you want the legacy ride-along behavior.
If a package upgrade adds a migration and you forget to run nightowl:migrate, php artisan nightowl:agent warns at startup that the schema is behind (and keeps running) rather than failing silently mid-drain. So wiring nightowl:migrate into your deploy is the way to keep the schema current.

Agent runtime

The async server enables SO_REUSEPORT automatically on Linux, so multiple agents can bind the same port without extra configuration — see Running multiple instances.

Authentication

For a standalone install, set NIGHTOWL_TOKEN only. For parallel mode, set both: NIGHTOWL_TOKEN is what the NightOwl agent verifies against, NIGHTWATCH_TOKEN is your Nightwatch token used by the Nightwatch SDK to reach Laravel Cloud’s hosted ingest in parallel.

Drain pipeline

Issue lifecycle

See throughput tuning for how to size these.

Sampling

Sampling is handled upstream by laravel/nightwatch before the payload reaches the agent. Configure it in your customer app’s .env:
The decision is made before transport, so the agent only sees and stores what Nightwatch decided to ship — no double-sampling. See Filtering and context for the full recipe.

Redaction

PII redaction is handled upstream by laravel/nightwatch before the payload reaches the agent. Configure it in your customer app’s .env:
Both lists are applied inside the customer’s PHP process before the record leaves the host, so the agent’s TCP buffer and PostgreSQL drain never see the un-redacted values.

Retention and caching

Full retention and pruning strategy lives in Data management.

Searchable log context

By default a log’s context is stored compressed. PostgreSQL cannot decompress it while running a query, so the dashboard’s log search matches the message only and shows a “Narrowed search” note saying so.
With this on, searching your logs for 8412 finds the line written by Log::error('Payment failed', ['order_id' => 8412]), not just lines with 8412 in the message. Restart the agent after changing it. The agent only stores context uncompressed once its schema has the column for it (added by php artisan nightowl:migrate, which the agent normally runs itself on boot). If the setting is on but the column is missing, the agent keeps storing compressed context, logs a warning saying so, and the dashboard keeps showing the “Narrowed search” note — nothing breaks, it just is not searchable yet. Run the migration and restart.
This trades storage for searchability, and how much depends entirely on what your app logs — small contexts barely compress at all (under about 256 bytes, compression can make them larger), while large ones compress well. Measure yours before deciding:
That reports what your context currently occupies and what it would occupy uncompressed, and changes nothing.

Making existing logs searchable

Turning the flag on is forward-only — it changes what the agent stores from that moment. Logs already on disk stay compressed and stay unsearchable, and because NightOwl retention is typically long, waiting for them to age out is not a real answer. Convert them:
It works newest-first, so the logs you are most likely to search become searchable first, and you can stop it at any time and re-run later to continue — an interrupted run leaves a smaller searchable range, never a wrong one. The dashboard keeps showing the “Narrowed search” note for ranges it has not reached yet. Useful options:
A full conversion rewrites every log row in range, which leaves dead rows behind until autovacuum (or --vacuum) reclaims them, so the table temporarily needs more disk than the figures above. On a large history, run it deliberately rather than during a deploy. --since is usually the better trade: most people search recent logs.

If the setting is turned off again

Removing NIGHTOWL_LOG_CONTEXT_SEARCHABLE (a deploy that drops the variable, for instance) makes the agent store compressed context again, and log search goes back to matching the message only — including over the history you converted. That data is not lost or changed; the search simply cannot cover a range that has compressed logs in it. To get it back, set the variable again, restart the agent, and re-run:
This second run is quick — your already-converted logs need no conversion, so it only re-establishes the searchable range over them.

Parallel mode

See Running alongside Nightwatch.

Artisan commands

The agent command accepts --driver=async|sync. async is the default and required for multi-worker drain; sync is a single-process fallback for hosts without pcntl/posix.