Drupal is a registered trademark of Dries Buytaert
cms 2.1.2 Update released for Drupal core (2.1.2)! drupal 11.1.10 Update released for Drupal core (11.1.10)! drupal 10.5.10 Update released for Drupal core (10.5.10)! drupal 10.4.10 Update released for Drupal core (10.4.10)! drupal 11.2.12 Update released for Drupal core (11.2.12)! drupal 11.3.10 Update released for Drupal core (11.3.10)! drupal 10.6.9 Update released for Drupal core (10.6.9)! drupal 10.6.8 Update released for Drupal core (10.6.8)! drupal 11.3.9 Update released for Drupal core (11.3.9)! drupal 11.3.8 Update released for Drupal core (11.3.8)! drupal 11.3.7 Update released for Drupal core (11.3.7)! drupal 11.2.11 Update released for Drupal core (11.2.11)! drupal 10.6.7 Update released for Drupal core (10.6.7)! drupal 10.5.9 Update released for Drupal core (10.5.9)! cms 2.1.1 Update released for Drupal core (2.1.1)! drupal 11.3.6 Update released for Drupal core (11.3.6)! drupal 10.6.6 Update released for Drupal core (10.6.6)! cms 2.1.0 Update released for Drupal core (2.1.0)! linkit 7.0.15 Minor update available for module linkit (7.0.15). views_data_export 8.x-1.10 Minor update available for module views_data_export (8.x-1.10).

pulsedeck

No security coverage
View on drupal.org

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

  1. Navigate to Administration > Configuration > Web services > Pulsedeck (/admin/config/services/pulsedeck).
  2. 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).
  3. In the "Monitoring services" section, click Add instance for each service you want to monitor.
  4. Configure each instance with its API URL, credentials, and an optional custom label.
  5. Enable the instance and click Save configuration.
  6. Use the Collect now button to verify the connection works.
  7. 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

  1. Go to Pulsedeck settings > "Standalone dashboard".
  2. Enter an access key (any string you choose).
  3. 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

  1. Create a package with "type": "pulsedeck-plugin" in composer.json.
  2. Add a PSR-4 autoload entry (e.g., "Drupal\\pulsedeck_myservice\\": "src/").
  3. Create a plugin class in src/Plugin/ServiceMonitor/ with the #[ServiceMonitor] attribute.
  4. Extend ServiceMonitorBase and implement collect(), buildWidget(), and buildConfigForm().

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.twig
  • pulsedeck-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 pulsedeck entries.

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=on on the ProxySet directive. See the "Apache configuration for SSE" section under Requirements.
  • Nginx: The X-Accel-Buffering: no header 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-status

    for consecutive failure counts.

  • Use Collect now to test the connection directly.

Plugin not discovered after installation

  • Run drush cr to rebuild the container and plugin caches.
  • Verify the package has "type": "pulsedeck-plugin" in its composer.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.

Activity

Total releases
1
First release
Apr 2026
Latest release
1 month ago
Release cadence
Stability
0% stable

Releases

Version Type Release date
1.0.x-dev Dev Apr 3, 2026