pagerlite-laravel maintained by pagerlite
PagerLite
A dead-simple, self-hosted on-call and paging system for Laravel — in the spirit of Horizon and Telescope. Install the package, visit /pagerlite, add your team, and drag someone onto the calendar: when an incident comes in, PagerLite notifies whoever is on call and escalates until somebody acknowledges. Everything lives in your own database; no data ever leaves your app.

Features
- Incident ingestion — a single
POSTendpoint any monitoring tool can hit, plus aPagerLite::notify()facade for reporting incidents from your own code. - On-call schedule — a drag-and-drop, day-based calendar. Drag a member onto a day, stretch the shift across as many days as you like. One person on call at a time.
- Team management — add and remove members without losing history: removing someone frees up their shifts from today onward, and restoring them brings back only their past shifts, outside the escalation chain until you place them again.
- Escalation chain — if the on-call member doesn't acknowledge within the timeout, the next member in the chain is paged, and so on until someone responds. Reorder the chain from the dashboard.
- Acknowledge & resolve — straight from the dashboard (with a confirmation step), plus a full per-incident timeline: who was paged, when it escalated, who acknowledged and resolved it.
- Member insights — every member has a page with their on-call history, days on call per month, and the incidents they were paged for.
- Embeddable calendar — share a read-only view of the schedule via a secret link, ready to iframe into a wiki or status page.
- Live updates — the dashboard, incident list, incident detail, and embedded calendar all poll in the background, so status changes and new incidents show up without a manual refresh.
- Email notifications — sent through your app's existing mail configuration. No third-party services.
Requirements
- PHP 8.4+
- Laravel 12
- A real queue driver (
database,redis, etc.) — escalation relies on delayed jobs firing on schedule, which thesyncdriver does not honor.
Installation
Require the package with Composer:
composer require pagerlite/pagerlite-laravel
Then run the install command:
php artisan pagerlite:install
It publishes the configuration and the pre-built dashboard assets, and offers to run the migrations (four pagerlite_-prefixed tables). Then:
-
Set a token in
.envso external tools can report incidents:PAGERLITE_TOKEN=your-long-random-secret -
Run a queue worker — escalation is driven by delayed queue jobs:
php artisan queue:work -
Visit
/pagerlite, add members, order the escalation chain, and put someone on call.
Dashboard authorization
Out of the box the dashboard is only accessible in the local environment. To open it up in other environments, register an authorization callback — typically in a service provider's boot method:
use Pagerlite\Laravel\PagerLite;
PagerLite::auth(function ($request) {
return $request->user()?->isAdmin() ?? false;
});
Acknowledge and resolve actions are attributed to your app's authenticated user.
Reporting incidents
From anywhere, over HTTP
curl -X POST https://your-app.test/pagerlite/api/events \
-H "Authorization: Bearer $PAGERLITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Server down", "message": "CPU at 100%", "service": "api"}'
title is required; message and service are optional. The endpoint responds 201 with the incident's id and status. For tools that can't set headers, the token may be passed in the URL instead: POST /pagerlite/api/events/{token}.
Every request creates a new incident — PagerLite does not deduplicate, so point your monitoring's state change hook at it, not its every-minute check.
From your own code
use Pagerlite\Laravel\PagerLite;
PagerLite::notify('Nightly backup failed', 'Disk full on backup volume', 'backups');
How paging works
- An incident is recorded, and the current on-call member (from the schedule) is notified.
- If nobody is on call, the escalation chain is paged from the top instead.
- If the incident isn't acknowledged within the escalation timeout (5 minutes by default), the next member of the escalation chain is notified — skipping whoever is already on call — until the chain is exhausted.
- Acknowledging the incident from the dashboard stops the escalation.
The schedule operates on UTC days: shifts hand over at midnight UTC, and every member sees the same calendar regardless of their timezone.
Embedding the calendar
Set a secret in .env:
PAGERLITE_EMBED_TOKEN=another-long-random-secret
The read-only calendar is then available — without logging in — at:
https://your-app.test/pagerlite/embed/calendar/{token}
Drop it into an iframe on a wiki or status page:
<iframe src="https://your-app.test/pagerlite/embed/calendar/your-token" width="100%" height="1000"></iframe>
Anyone with the link can see who is on call (names and schedule only — no incidents, no editing). Leave PAGERLITE_EMBED_TOKEN unset and the embed URL does not exist.
Configuration
The published config/pagerlite.php covers everything; each option also has an environment variable:
| Variable | Default | Purpose |
|---|---|---|
PAGERLITE_PATH |
pagerlite |
URI path where the dashboard is served. |
PAGERLITE_TOKEN |
— | Bearer token required by the events endpoint. |
PAGERLITE_EMBED_TOKEN |
— | Secret enabling the public read-only calendar. Unset = disabled. |
PAGERLITE_ESCALATION_TIMEOUT |
300 |
Seconds to wait for an acknowledgement before escalating. |
PAGERLITE_USER_MODEL |
App\Models\User |
Host-app user model that acknowledge/resolve actions are attributed to. |
Upgrading
After updating the package, re-publish the dashboard assets:
php artisan vendor:publish --tag=pagerlite-assets --force
(or simply re-run php artisan pagerlite:install).
TODO
- Add more notification channels
- SMS
- Discord
- Slack
- Telegram
- Webhook
- Mobile app
- Unified phone number that routes to whoever is on call
License
PagerLite is open-source software licensed under the MIT license.