pulsedeck
Pulsedeck provides a real-time infrastructure monitoring dashboard for Drupal. It displays lightweight, auto-updating widgets for external services like uptime monitors, alert managers, server monitors, network tools, security engines, and development platforms.
Data collection runs server-side via cron, Drush commands, or a long-running daemon process. Widgets update in real time through Server-Sent Events (SSE) using Drupal core's built-in HTMX support. The browser never contacts external services directly.
Each service integration is a plugin distributed as a standalone Composer package. Install plugins with composer require -- no module enabling needed. See the Pulsedeck ecosystem for all available plugins.
For a full description of the module, visit the project page.
Submit bug reports and feature suggestions, or track changes in the issue queue.
Requirements
This module requires no modules outside of Drupal core.
- Drupal 11.3 or later (HTMX support in core is required)
- PHP 8.3 or later
Apache configuration for SSE
When running behind Apache with mod_proxy_fcgi (e.g., the wodby/apache Docker image), you must enable packet flushing so that SSE data reaches the browser in real time instead of being buffered:
ProxySet connectiontimeout=5 timeout=60 flushpackets=on
For wodby/apache, set this environment variable on the Apache service:
environment:
APACHE_FCGI_PROXY_TIMEOUT: "60 flushpackets=on"
Without flushpackets=on, SSE events are held in Apache's proxy buffer and only reach the browser when the connection closes. Nginx does not need this -- the X-Accel-Buffering: no header (sent automatically) is sufficient.
Service plugins
Pulsedeck requires one or more service monitor plugins to populate the dashboard. Plugins are Composer packages, not Drupal modules. Browse all available plugins on the Pulsedeck ecosystem page.
Example installation:
composer require drupal/pulsedeck_uptime_kuma
composer require drupal/pulsedeck_healthchecks
drush cr
Plugins are discovered automatically via Composer -- no drush en needed.
Installation
Install as you would normally install a contributed Drupal module. For further information, see Installing Drupal Modules.
composer require drupal/pulsedeck
drush en pulsedeck
Configuration
- Navigate to Administration > Configuration > Web services > Pulsedeck (
/admin/config/services/pulsedeck). - Adjust the general settings:
- Default collection interval: How often data is fetched from services (in seconds, default 60).
- SSE session timeout: How long the real-time connection stays open before the browser reconnects (default 30 seconds).
- Data retention: How many days of historical snapshots to keep (default 30 days, cleaned up during cron).
- In the "Monitoring services" section, click Add instance for each service you want to monitor.
- Configure each instance with its API URL, credentials, and an optional custom label.
- Enable the instance and click Save configuration.
- Use the Collect now button to verify the connection works.
- Visit the dashboard at Administration > Reports > Pulsedeck (
/admin/reports/pulsedeck).
Permissions
- Administer Pulsedeck: Configure settings and manage service connections.
- Access Pulsedeck dashboard: View the monitoring dashboard.
Multiple instances
Each plugin supports multiple instances. For example, you can monitor two separate Uptime Kuma installations by adding two instances of the Uptime Kuma plugin, each with its own URL and credentials.
Widget ordering
Widgets can be reordered on the dashboard by dragging and dropping. The order is persisted automatically. You can also set a numeric weight per instance in the configuration form.
Acknowledging alerts
When a service shows a warning or critical status, you can acknowledge it by clicking the "Acknowledge" link in the widget footer. This suppresses the alert for the specific items that are currently down.
- If an acknowledged item recovers, the acknowledgement scope narrows to the remaining items. No alert is raised for the recovery itself.
- If a recovered item goes down again, it is treated as a new incident and the alert reappears.
- If an entirely new item goes down, the alert fires immediately.
- Acknowledgements are automatically cleared when the service returns to a fully healthy state.
Dashboard features
The dashboard shows a summary header with:
- Overall health status indicator
- Count badges per status level (OK, warning, critical)
- Last data collection timestamp
- SSE connection indicator (green dot when live updates are active)
- Manual refresh button
OK services are displayed as compact cards in a sidebar column. Services that need attention are shown as expanded cards in the main grid area. All cards are collapsible by clicking the header.
The dashboard supports Gin admin theme dark mode.
Standalone dashboard
The dashboard can be accessed without Drupal login using an access key. This enables kiosk displays, desktop apps, and embedding.
Setup
- Go to Pulsedeck settings > "Standalone dashboard".
- Enter an access key (any string you choose).
- The standalone URL is shown after saving:
https://your-site.com/pulsedeck/standalone?key=YOUR_KEY
The standalone page renders the full dashboard with all assets (HTMX, SSE, drag-and-drop) but without Drupal page chrome. It supports dark mode via @media (prefers-color-scheme: dark).
Linux desktop app
A lightweight Python/GTK app is included in apps/linux/. It displays the standalone dashboard in a native window without browser chrome.
Requirements:
# Debian/Ubuntu
sudo apt install python3-gi gir1.2-gtk-4.0 gir1.2-webkit-6.0
# Fedora
sudo dnf install python3-gobject gtk4 webkitgtk6.0
Run from source:
./apps/linux/pulsedeck 'https://your-site.com/pulsedeck/standalone?key=YOUR_KEY'
Install system-wide:
sudo cp apps/linux/pulsedeck /usr/local/bin/
sudo cp apps/linux/pulsedeck.svg /usr/share/icons/hicolor/scalable/apps/
sudo cp apps/linux/pulsedeck.desktop /usr/share/applications/
sudo gtk-update-icon-cache /usr/share/icons/hicolor/
Options:
-
--dark-- Force dark mode regardless of system preference.
On first run, the app automatically installs its icon and desktop entry to ~/.local/share/ for proper taskbar integration on Wayland.
Android app
A native Android app is included in apps/android/. It wraps the standalone dashboard in a WebView with automatic dark mode support.
Build (requires Android SDK + Java 17):
cd apps/android
./gradlew assembleDebug
Install the APK from app/build/outputs/apk/debug/app-debug.apk.
On first launch, tap the screen to configure your standalone URL. The URL is saved in app preferences. External links open in the default browser. See apps/android/README.md for details.
Creating custom plugins
Pulsedeck uses Drupal's attribute-based plugin system. Any Composer package or Drupal module can provide ServiceMonitor plugins.
As a standalone Composer package
- Create a package with
"type": "pulsedeck-plugin"incomposer.json. - Add a PSR-4 autoload entry (e.g.,
"Drupal\\pulsedeck_myservice\\": "src/"). - Create a plugin class in
src/Plugin/ServiceMonitor/with the#[ServiceMonitor]attribute. - Extend
ServiceMonitorBaseand implementcollect(),buildWidget(), andbuildConfigForm().
Example composer.json:
{
"name": "drupal/pulsedeck_myservice",
"type": "pulsedeck-plugin",
"autoload": {
"psr-4": {
"Drupal\\pulsedeck_myservice\\": "src/"
}
},
"extra": {
"pulsedeck": {
"namespace": "Drupal\\pulsedeck_myservice"
}
}
}
As a Drupal module
Place your plugin class in your module's src/Plugin/ServiceMonitor/ directory. It will be discovered automatically when your module is enabled.
Template overrides
Themes can override widget templates per plugin or per instance:
pulsedeck-widget--uptime-kuma.html.twigpulsedeck-widget--uptime-kuma--production.html.twig
Drush commands
Command
Alias
Description
pulsedeck:plugins
pd-plugins
List available plugins
pulsedeck:status
pd-status
Show all instances with health status
pulsedeck:collect
pd-collect
Collect data immediately
pulsedeck:daemon
pd-daemon
Long-running collection loop
Examples:
drush pd-plugins
drush pd-status
drush pd-collect uptime_kuma
drush pd-collect all
drush pd-collect uptime_kuma --instance=production
drush pd-daemon # 10s cycle (default)
drush pd-daemon --sleep=5 # 5s cycle
Data collection strategies
Cron (simplest): Pulsedeck enqueues collection tasks during cron. Run drush cron periodically (e.g., every minute via system crontab). Each invocation bootstraps Drupal.
One-shot command: drush pd-collect all collects from all services directly, bypassing the queue. Useful in crontab entries where you want per-minute collection without running all of Drupal's cron tasks.
Daemon (recommended for production): drush pd-daemon bootstraps Drupal once and loops continuously, collecting from services as they become due based on each plugin's configured interval. This avoids the overhead of repeated Drupal bootstraps and provides the lowest latency between service state changes and dashboard updates.
Run the daemon as a Docker service, systemd unit, or supervisord program. Example docker-compose service:
pulsedeck:
image: 'wodby/drupal-php:8.4'
depends_on:
- mariadb
restart: unless-stopped
command: >
sh -c '
echo "Waiting for database...";
sleep 10;
vendor/bin/drush pulsedeck:daemon --sleep=10
'
working_dir: /var/www/html
environment:
DB_HOST: mariadb
DB_USER: drupal
DB_PASSWORD: drupal
DB_NAME: drupal
DB_DRIVER: mysql
PHP_XDEBUG: 0
volumes:
- ./:/var/www/html
The restart: unless-stopped policy ensures Docker restarts the container if the daemon crashes.
Troubleshooting
No data collected after running cron
- Verify the service instance is enabled in the settings form.
- Check that cron is running (
drush cron). - Use the Collect now button on the settings page to trigger an immediate collection and see if errors appear.
- Check the database log at Administration > Reports > Recent log messages for
pulsedeckentries.
Dashboard shows "No data collected yet"
- Run cron at least once after configuring a service. The first cron run enqueues the collection task; the same cron run processes it.
- If using
drush cron, both enqueueing and processing happen in a single run.
Widgets not updating in real time
- Check the SSE indicator dot in the dashboard header -- green means connected, gray means disconnected.
-
Apache + mod_proxy_fcgi: You must set
flushpackets=onon the ProxySet directive. See the "Apache configuration for SSE" section under Requirements. -
Nginx: The
X-Accel-Buffering: noheader is sent automatically and should be sufficient. - Shared hosting: SSE requires long-lived HTTP connections. Some environments may terminate connections early. The dashboard falls back to HTMX polling when SSE is unavailable.
-
Twig debug: When Twig debug mode is enabled, the SSE controller strips debug comments from rendered HTML. If you see widgets not updating, check that the SSE events contain clean HTML without
</code> comments.</li> </ul> <h3 id="user-content-widget-shows-stale-data-warning">Widget shows "Stale data" warning<a href="#widget-shows-stale-data-warning" aria-label="Link to heading 'Widget shows " stale data warning data-heading-content='Widget shows "Stale data" warning' class="anchor"></a> </h3> <ul> <li>The service data is older than twice the collection interval. This usually means cron stopped running or the service's API is not responding.</li> <li>Check <code>drush pd-statusfor consecutive failure counts.
- Use Collect now to test the connection directly.
Plugin not discovered after installation
- Run
drush crto rebuild the container and plugin caches. - Verify the package has
"type": "pulsedeck-plugin"in itscomposer.json.
FAQ
Q: Does Pulsedeck depend on any JavaScript frameworks?
A: No. It uses Drupal core's built-in HTMX (available from 11.3) and a small bundled SSE extension (~4KB). No React, Vue, or other framework is loaded.
Q: How many concurrent dashboard users can it support?
A: Each open browser tab holds one PHP-FPM worker for the SSE session duration (default 30 seconds, then reconnects). With 5 admin tabs open, 5 workers are occupied. For most admin dashboards this is negligible. Reduce the SSE session timeout if you need to conserve workers.
Q: Can I use this with Drupal's contrib Dashboard module?
A: Pulsedeck provides its own dashboard page. It does not integrate with the contrib Dashboard module, which depends on Layout Builder, Node, and Views -- adding dependencies that conflict with Pulsedeck's minimal-dependency approach.
Q: Where is monitoring data stored?
A: In two custom database tables (pulsedeck_snapshot and pulsedeck_service_state). Old data is cleaned up automatically during cron based on the data retention setting.