skip to content

Why does the Laravel Pulse Servers card stay empty after installation, and what does the pulse:check command do on each server?

level: middleimportance: nice to knowfreq 12%

answer

  1. event-driven recorders vs polling ones
  2. pulse:check beats every second
  3. snapshot throttled to 15 seconds
  4. PULSE_SERVER_NAME defaults to gethostname()
  5. Supervisor, then pulse:restart on deploy

basics

~20 s

The Servers card is fed by the Servers recorder, which only reacts to beats emitted by php artisan pulse:check; until that long-running command runs on each server, nothing records CPU, memory or disk. Each server needs a unique PULSE_SERVER_NAME.

solid answer

~50 s

Most Pulse recorders listen to framework events (requests, queries, jobs), so they fill in on their own. The `Servers` recorder is different: it listens to Pulse's `SharedBeat` event, and only `php artisan pulse:check` dispatches that, once per second. The recorder throttles itself to one snapshot per 15 seconds per `pulse:check` process, reading CPU and memory through OS tools and disk usage for the directories in `PULSE_SERVER_DIRECTORIES` (default `/`). It stores them under the server name, which defaults to `gethostname()` and can be set with `PULSE_SERVER_NAME`; two machines with the same name overwrite each other. So you run `pulse:check` on every application server under a process monitor, and call `pulse:restart` during deploys so it picks up new code. The same process also emits an `IsolatedBeat` on whichever server wins a one-second cache lock, for cards that should poll from only one machine.

go deeper

for a junior

Recall that the Servers card needs php artisan pulse:check running on each server, kept alive by a process monitor.

for a middle

Explain event-driven versus polling recorders, the one-second beats, the 15-second snapshot throttle, and why server names must be unique.

for a senior

Operate it across a fleet: unique names in containers, a shared lock-capable cache, pulse:restart on deploy, and ignore-after for retired servers.

for a principal

Decide whether Pulse's server card is enough for capacity signals or whether host metrics belong in dedicated infrastructure monitoring.

## Two kinds of recorders **Laravel Pulse** recorders capture data in one of two ways: - **Event-driven** recorders hook into things the app already does: `SlowRequests` and `UserRequests` hook the HTTP kernel, `SlowQueries` listens to `QueryExecuted`, `SlowJobs` and `Queues` listen to queue events, `Exceptions` to reported exceptions. They start filling their cards as soon as traffic arrives. - **Polling** recorders need something to wake them up regularly. The built-in one is `Servers`, and some third-party cards work the same way. The Servers card stays empty after installation because nothing is waking its recorder yet. ## What pulse:check does `php artisan pulse:check` is a long-running loop. On each iteration, about once per second, it: 1. Exits if the restart signal in the cache (`laravel:pulse:restart`) has changed since it started. 2. Tries a one-second cache lock named `laravel:pulse:check`; if it gets it, it dispatches `IsolatedBeat`, meant for work that should happen on only one server. 3. Dispatches `SharedBeat` with the current time and a random instance id, meant for work every server should do. 4. Calls `Pulse::ingest()` to flush what the recorders captured. `--once` takes a single snapshot and exits. When the command detects Vapor (the `VAPOR_SSM_PATH` variable), it also runs once, because there is no long-lived server to monitor there. ## What the Servers recorder stores The `Servers` recorder listens to `SharedBeat` and throttles itself to one snapshot every **15 seconds** per `pulse:check` instance, using the Pulse cache store. Each snapshot records: - CPU usage and used memory as averaged aggregates (for the card's graphs); - a `system` value holding the server's display name, current CPU, used and total memory, and used and total space for each directory in `PULSE_SERVER_DIRECTORIES` (colon-separated, default `/`). CPU and memory are read through operating-system tools via `shell_exec` on Linux, macOS, Windows and BSD; on any other OS family the recorder throws. Hosts that disable `shell_exec` can plug in their own readers with `Servers::detectCpuUsing()` and `Servers::detectMemoryUsing()`. ## Naming servers | Setting | Env variable | Default | |---|---|---| | `server_name` | `PULSE_SERVER_NAME` | PHP's `gethostname()` | | `directories` | `PULSE_SERVER_DIRECTORIES` | `/` | The name becomes the key under which snapshots are stored, so **every reporting server must have a unique name**. Containers or cloned images that share a hostname appear as one server, flickering between their readings. When you retire a server, the card keeps showing it; the `ignore-after` prop, such as `<livewire:pulse.servers ignore-after="3 hours" />`, hides servers that stopped reporting. ## Running it in production - Run `pulse:check` on **every** application server you want on the card, not just one. - Keep it alive with a process monitor such as Supervisor, as the docs recommend. - Run `php artisan pulse:restart` during each deploy; it writes a new restart timestamp to the cache, and each `pulse:check` exits on its next loop, to be restarted with the new code. - Use a cache store shared by all servers, because both the restart signal and the `IsolatedBeat` lock live there; the lock only works on a store that supports locks. ## Beyond the built-in card The beat events are also the extension point for polling cards. A custom recorder can declare a `$listen` property of `SharedBeat::class` to measure something on every server, or listen to `IsolatedBeat` to poll a shared resource, such as an external API's quota, exactly once across the fleet. The built-in `Servers` recorder shows the pattern for keeping that cheap: it throttles itself so a once-per-second beat produces a snapshot at most every 15 seconds. Whatever a custom recorder captures goes through the normal `Pulse::record()` or `Pulse::set()` calls and the configured ingest, so the rest of the pipeline is unchanged.

  • Two servers report to one Pulse dashboard but the Servers card shows only one; what do you check?
    Their server names. The recorder stores snapshots under `PULSE_SERVER_NAME`, defaulting to `gethostname()`, so containers or cloned machines with the same hostname overwrite each other's row. Give each server a unique `PULSE_SERVER_NAME`, and confirm `pulse:check` is actually running on both.
  • What is the difference between Pulse's SharedBeat and IsolatedBeat events?
    `pulse:check` dispatches `SharedBeat` on every server, every loop, for work each machine must do, such as the Servers snapshot. It dispatches `IsolatedBeat` only on the server that wins a one-second `laravel:pulse:check` cache lock, for polling that should happen once across the fleet; that needs a lock-capable cache shared by all servers.

saying these in an interview costs you the question

  • The Servers card fills in automatically from normal HTTP traffic.
  • Running pulse:check on one server is enough to monitor the whole fleet.
  • pulse:check records a CPU snapshot every second.
  • Cloned servers with the same hostname appear as separate rows.
  • pulse:check notices new code after a deploy without a restart.