How to Make Custom Dashlets in Nagios XI
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 themeReplace 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:
| Option | Description |
|---|---|
| DASHLET_NAME | Unique internal identifier (must be lowercase and unique). |
| DASHLET_TITLE | Display title in the UI. |
| DASHLET_DESCRIPTION | Brief purpose, shown in the UI (supports HTML). |
| DASHLET_VERSION | Version number (e.g., ‘1.0.0’). |
| DASHLET_DATE | Release date (e.g., ’09/04/2025′). |
| DASHLET_AUTHOR | Author or organization name. |
| DASHLET_COPYRIGHT | Copyright notice. |
| DASHLET_LICENSE | License type (e.g., ‘BSD’). |
| DASHLET_HOMEPAGE | URL to homepage or docs. |
| DASHLET_REFRESHRATE | Default refresh rate in seconds. |
| DASHLET_FUNCTION | PHP function name for rendering. |
| DASHLET_FILTER_GROUPS | Array of groups for filtering in “Available Dashlets” (e.g., array(‘custom’)). |
| DASHLET_OUTBOARD_CLASS | CSS class for outboard view. |
| DASHLET_INBOARD_CLASS | CSS class for inboard view. |
| DASHLET_PREVIEW_CLASS | CSS class for preview thumbnail. |
| DASHLET_WIDTH | Suggested default width in pixels. |
| DASHLET_HEIGHT | Suggested default height in pixels. |
| DASHLET_OPACITY | Opacity level (e.g., ‘1.0’). |
| DASHLET_PREVIEW | Path 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_nameEnsure 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
- Navigate to Admin > System Extensions > Manage Dashlets.
- Click Browse, select your ZIP, and Upload & Install.
- 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-2024Here 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
- Go to Dashboards > Available Dashlets.
- Locate your dashlet (e.g., Top Failing Checks (Last Hour)) and click Add This to a Dashboard.
- Select a dashboard, add a title (optional), and click Add It.
- 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
- Go to Dashboards > Available Dashlet > Grouped > Custom.
- Select your Dashlet (e.g., Top Failing Checks (Last Hour)) and click Submit.
- Your Dashboard will automatically appear in the dashboard.
- On the dashboard, resize, move, or set refresh intervals.

Variations and Customizations
- Current Failures View: Query
nagios_servicestatuswithcurrent_state IN (1,2,3)for real-time status instead of counts. - Adjust Time Window: Replace
INTERVAL 1 HOURwithINTERVAL 24 HOURor 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_PARAMSin registration for inputs like time windows, read viagrab_request_var(). - REST API Alternative: Use
/nagiosxi/api/v1/objects/servicestatusfor 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_servicechecksfor 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.phpregistering 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.



