How to Make Custom Dashlets in Nagios XI

Picture of Ayub Huruse
Ayub Huruse
IT Specialist
Nagios XI dashlets.

Nagios XI lets you build custom dashlets for your monitoring goals. Dashlets place the metrics, visuals, and insights you need on the dashboard where your team can act. This guide shows you how to build, package, install, and customize a dashlet. For illustration, we’ll use a practical example: a dashlet that displays the top 10 problem services in the last hour, which helps identify services frequently entering non-OK states (WARNING, CRITICAL, or UNKNOWN).

Prerequisites

  • Nagios XI admin access.
  • Shell access to the XI server (optional for ZIP uploads via UI; recommended for packaging and troubleshooting).
  • Basic knowledge of PHP, SQL (if querying the database), and HTML.

Default Install Path: Dashlets live in /usr/local/nagiosxi/html/includes/dashlets/.

Step 1: Create the Dashlet Folder

Custom dashlets in Nagios XI are organized in dedicated folders under the dashlets directory. This structure keeps your code modular and easy to manage.

On your XI server (or locally, then ZIP and upload), set up a folder for your dashlet with the following core files:

/usr/local/nagiosxi/html/includes/dashlets/your_dashlet_name/
├─ your_dashlet_name.inc.php                      # Contains registration and rendering logic
├─ preview.png                                    # Default preview thumbnail
├─ neptune_preview.png                            # Preview for Neptune theme
├─ your_dashlet_name_neptune_light_preview.png    # Preview for Neptune Light theme

Replace your_dashlet_name with a unique, lowercase identifier (e.g., top_fails_last_hour for our example).

Removal Tip: To uninstall a faulty dashlet manually, delete its subfolder under
/usr/local/nagiosxi/html/includes/dashlets/ and reload the XI UI.

Step 2: Dashlet Code File

Custom dashlets are defined in a single .inc.php file that handles registration and rendering (main). Include dashlethelper.inc.php, define an initialization function to register the dashlet and a rendering function to handle output based on modes (e.g., preview, inboard/outboard display).

Example (top_problem_services_last_hour.inc.php):

<?php
//
// Top Problem Services Dashlet
// Copyright (c) 2025 Your Name. All rights reserved.
//

include_once(dirname(__FILE__) . '/../dashlethelper.inc.php');

// Run the initialization function
top_problem_services_last_hour_init();

function top_problem_services_last_hour_init() {
    $name = 'top_problem_services_last_hour';

    $args = array(
        DASHLET_NAME        => $name,
        DASHLET_TITLE       => _('Top Problem Services (Last Hour)'),
        DASHLET_DESCRIPTION => _('Shows the 10 services with the most non-OK check results in the past hour (WARNING/CRITICAL/UNKNOWN).'),
        DASHLET_VERSION     => '1.0.0',
        DASHLET_DATE        => '09/04/2025',
        DASHLET_AUTHOR      => 'Your Name',
        DASHLET_COPYRIGHT   => '© ' . date('Y') . ' Your Name',
        DASHLET_LICENSE     => 'BSD',
        DASHLET_HOMEPAGE    => 'https://example.com',
        DASHLET_REFRESHRATE => 60,
        DASHLET_FUNCTION    => 'top_problem_services_last_hour_func',
        DASHLET_FILTER_GROUPS => array('custom'),
        DASHLET_OUTBOARD_CLASS => 'custom_outboardclass',
        DASHLET_INBOARD_CLASS  => 'custom_inboardclass',
        DASHLET_PREVIEW_CLASS  => 'custom_previewclass',
        DASHLET_WIDTH       => '520',
        DASHLET_HEIGHT      => '360',
        DASHLET_OPACITY     => '1.0',
        // Optional preview image helps identify it in “Available Dashlets”
        DASHLET_PREVIEW     => get_dashlets_base_url() . 'top_problem_services_last_hour/img/preview.png',
    );

    register_dashlet($name, $args);
}

function top_problem_services_last_hour_func($mode = DASHLET_MODE_PREVIEW, $id = '', $args = null) {
    $output = '';

    switch ($mode) {
        case DASHLET_MODE_GETCONFIGHTML:
            // No configuration needed for this dashlet
            $output = '';
            break;

        case DASHLET_MODE_OUTBOARD:
        case DASHLET_MODE_INBOARD:
            // Renderer logic: Fetch data and generate HTML
            require_once(dirname(__FILE__) . '/../db.inc.php'); // Provides exec_sql_query() and DB_* constants

            dashlets_data_check(); // Basic context/auth guard

            // SQL notes:
            // - Table names have a default prefix "nagios_" (e.g., nagios_servicechecks).
            // - States: 0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN.

            $sql = "
            SELECT
              o.name1  AS host_name,
              o.name2  AS service_desc,
              COUNT(*) AS problem_count,
              MAX(sc.start_time) AS last_problem
            FROM nagios_servicechecks sc
            JOIN nagios_objects o
              ON o.object_id = sc.service_object_id
            WHERE sc.start_time >= NOW() - INTERVAL 1 HOUR
              AND sc.state IN (1,2,3)
            GROUP BY sc.service_object_id, o.name1, o.name2
            ORDER BY problem_count DESC, last_problem DESC
            LIMIT 10";

            $rs = exec_sql_query(DB_NDOUTILS, $sql);
            $rows = [];
            if ($rs && $rs->num_rows() > 0) {
                while ($r = $rs->fetch_assoc()) {
                    $rows[] = $r;
                }
            }

            // HTML output (heredoc for multi-line string)
            $output = <<<OUTPUT
<div class="pad-lr" style="font-size:13px;">
  <h3 style="margin:6px 0 10px;">Top 10 Problem Services (Last 60 Minutes)</h3>
  <?php if (empty(\$rows)) : ?>
    <p>No problem services in the last hour.</p>
  <?php else: ?>
  <table class="table table-condensed table-striped" style="width:100%; border-collapse:collapse;">
    <thead>
      <tr>
        <th style="text-align:left; padding:6px; border-bottom:1px solid #ddd;">#</th>
        <th style="text-align:left; padding:6px; border-bottom:1px solid #ddd;">Host</th>
        <th style="text-align:left; padding:6px; border-bottom:1px solid #ddd;">Service</th>
        <th style="text-align:right; padding:6px; border-bottom:1px solid #ddd;">Problems</th>
        <th style="text-align:left; padding:6px; border-bottom:1px solid #ddd;">Last Problem</th>
      </tr>
    </thead>
    <tbody>
      <?php \$i=1; foreach (\$rows as \$r): ?>
      <tr>
        <td style="padding:6px; border-bottom:1px solid #eee;"><?php echo (int)\$i++; ?></td>
        <td style="padding:6px; border-bottom:1px solid #eee;"><?php echo htmlspecialchars(\$r['host_name']); ?></td>
        <td style="padding:6px; border-bottom:1px solid #eee;"><?php echo htmlspecialchars(\$r['service_desc']); ?></td>
        <td style="padding:6px; text-align:right; border-bottom:1px solid #eee;"><?php echo (int)\$r['problem_count']; ?></td>
        <td style="padding:6px; border-bottom:1px solid #eee;"><?php echo htmlspecialchars(\$r['last_problem']); ?></td>
      </tr>
      <?php endforeach; ?>
    </tbody>
  </table>
  <?php endif; ?>
  <div style="margin-top:6px; opacity:.8; font-size:12px;">
    <em>Tips:</em> Adjust refresh in the dashlet settings. Counts are based on individual check results during the last 60 minutes.
  </div>
</div>
OUTPUT;
            break;

        case DASHLET_MODE_PREVIEW:
            $output = '<p>Preview of Top 10 Problem Services (Last Hour)</p>';
            break;
    }

    return $output;
}

Key Notes:

  • Customize fields like DASHLET_TITLE and DASHLET_DESCRIPTION.
  • The initialization function registers the dashlet with a unique name and points to the rendering function via DASHLET_FUNCTION.
  • dashlethelper.inc.php provides registration utilities.
  • Width and height are suggestions; users can resize.

Here’s a quick reference table for common registration options:

OptionDescription
DASHLET_NAMEUnique internal identifier (must be lowercase and unique).
DASHLET_TITLEDisplay title in the UI.
DASHLET_DESCRIPTIONBrief purpose, shown in the UI (supports HTML).
DASHLET_VERSIONVersion number (e.g., ‘1.0.0’).
DASHLET_DATERelease date (e.g., ’09/04/2025′).
DASHLET_AUTHORAuthor or organization name.
DASHLET_COPYRIGHTCopyright notice.
DASHLET_LICENSELicense type (e.g., ‘BSD’).
DASHLET_HOMEPAGEURL to homepage or docs.
DASHLET_REFRESHRATEDefault refresh rate in seconds.
DASHLET_FUNCTIONPHP function name for rendering.
DASHLET_FILTER_GROUPSArray of groups for filtering in “Available Dashlets” (e.g., array(‘custom’)).
DASHLET_OUTBOARD_CLASSCSS class for outboard view.
DASHLET_INBOARD_CLASSCSS class for inboard view.
DASHLET_PREVIEW_CLASSCSS class for preview thumbnail.
DASHLET_WIDTHSuggested default width in pixels.
DASHLET_HEIGHTSuggested default height in pixels.
DASHLET_OPACITYOpacity level (e.g., ‘1.0’).
DASHLET_PREVIEWPath to preview image.

How It Works:

  • Include helpers for auth and data.
  • The rendering function handles modes: GETCONFIGHTML (for user inputs, if any), OUTBOARD/INBOARD (main display), PREVIEW (thumbnail view).
  • Fetch data via SQL on Nagios tables.
  • Render HTML (tables, charts via JS, etc.).
  • Security Note: Use htmlspecialchars() to prevent XSS. Parameterize queries. Nagios XI uses MySQL by default; adjust for PostgreSQL.
  • Timezone Note: NOW() uses server time; align OS/DB timezones.

For Production (Prepared Statements Example): In the rendering function, replace exec_sql_query() with PDO:

$db = get_ndo_db_connection(); // Assuming PDO handle from db.inc.php
$stmt = $db->prepare($sql);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Step 3: Package as ZIP

To distribute or install your dashlet, package the folder as a ZIP file. This is the standard format for uploading via the Nagios XI UI.

From the dashlets directory:

cd /usr/local/nagiosxi/html/includes/dashlets
zip -r your_dashlet_name.zip your_dashlet_name

Ensure the ZIP’s root is the folder (e.g., your_dashlet_name/…), not just files. Build locally if preferred.

Step 4: Install via Web UI

  1. Navigate to Admin > System Extensions > Manage Dashlets.
  2. Click Browse, select your ZIP, and Upload & Install.
  3. Verify it appears in the list without errors.

For a visual walkthrough of installation and management, watch this video:

Uninstall method for UI: Use the delete icon in Manage Dashlets, or manually remove the folder and refresh.

You can also refer to this documentation for full details:

Installing-Dashlets-in-Nagios-XI-2024

Here is a direct link to the PDF as well:

Installing-Dashlets-in-Nagios-XI-2024.

Step 5: Add to a Dashboard

Step 5A: Legacy Method

  1. Go to Dashboards > Available Dashlets.
  2. Locate your dashlet (e.g., Top Failing Checks (Last Hour)) and click Add This to a Dashboard.
  3. Select a dashboard, add a title (optional), and click Add It.
  4. On the dashboard, resize, move, or set refresh intervals.

For a visual walkthrough of adding dashlets to dashboards in Nagios XI:

Step 5B: Smart Method

  1. Go to Dashboards > Available Dashlet > Grouped > Custom.
  2. Select your Dashlet (e.g., Top Failing Checks (Last Hour)) and click Submit.
  3. Your Dashboard will automatically appear in the dashboard.
  4. On the dashboard, resize, move, or set refresh intervals.
Add dashlet screen in Nagios XI
Smart method for adding dashlet

Variations and Customizations

  • Current Failures View: Query nagios_servicestatus with current_state IN (1,2,3) for real-time status instead of counts.
  • Adjust Time Window: Replace INTERVAL 1 HOUR with INTERVAL 24 HOUR or custom values.
  • Add Links: Link host/service to details:
<a href="/nagiosxi/includes/components/xicore/status.php?show=servicedetail&host=
<?php echo urlencode($r['host_name']); ?>&service=<?php echo
urlencode($r['service_desc']); ?>">...</a>
  • User Parameters: Add DASHLET_PARAMS in registration for inputs like time windows, read via grab_request_var().
  • REST API Alternative: Use /nagiosxi/api/v1/objects/servicestatus for client-side aggregation; SQL is better for history.

For a visual walkthrough of how to customize dashboards in Nagios XI, watch this video:

Performance and Security Best Practices

  • Efficiency: Keep queries light; add indexes to nagios_servicechecks for large datasets.
  • Security: Sanitize all outputs and inputs. Only admins can upload dashlets and audit code for vulnerabilities. Ensure PHP 8+ compatibility.
  • Scalability: Avoid long-running scripts; use caching for frequent refreshes.

Troubleshooting

  • Dashlet Not Appearing: Ensure ZIP has a top-level folder with .inc.php registering correctly.
  • Errors or White Screen: Delete the folder and check PHP logs (e.g.,/var/log/httpd/error_log).
  • Empty Table: Verify recent failures exist; check DB prefix (default: nagios_).
  • Additional: If file permissions are an issue, check SELinux/AppArmor logs. For API issues, confirm keys and endpoints.

Conclusion

You’ve now built and deployed a custom dashlet that enhances Nagios XI’s monitoring
capabilities. This example is a gateway to more advanced customization experiments
with APIs, charts, or integrations. Share your variations in the comments, and remember:
effective monitoring starts with visibility. If you encounter issues, test iteratively and
leverage the community.

Share: