Skip to content

Passive Monitoring with Icinga 2

Goal: Have NSClient++ run checks on a schedule and submit the results to an Icinga 2 server through its REST API — without Icinga 2 needing to poll each Windows host directly.

Tip

The Icinga 2 client uses HTTPS and the standard Icinga 2 REST API (/v1/actions/process-check-result). It is a good fit when your monitoring server runs Icinga 2, the agents are behind a firewall, or you cannot install the Icinga agent on the Windows hosts.


How Passive Monitoring Works

In active (NRPE / agent) monitoring, the monitoring server initiates contact and polls the agent:

Icinga 2 Server ──check_nrpe──► NSClient++ → runs check → returns result

In passive monitoring with the Icinga 2 REST API, the agent runs checks on its own schedule and submits the results over HTTPS:

NSClient++ (Scheduler) → runs check → IcingaClient ──HTTPS──► Icinga 2 (/v1/actions/process-check-result)

The Icinga 2 server simply waits for results. If no result arrives within the expected time window, Icinga 2 raises a freshness alert.


Prerequisites

Enable these modules in nsclient.ini:

[/modules]
CheckSystem = enabled   ; provides check_cpu, check_memory, etc.
CheckDisk = enabled     ; provides check_drivesize
Scheduler = enabled     ; runs checks on a timer
IcingaClient = enabled  ; submits results to the Icinga 2 REST API
CheckHelpers = enabled  ; provides the check_ok command

You also need an Icinga 2 user with the right permissions. On the Icinga 2 server, add an ApiUser that is allowed to run the process-check-result action and (optionally) create objects:

object ApiUser "nscp" {
    password = "secret-password"
    permissions = [
        "actions/process-check-result",
        "objects/query/Host",
        "objects/query/Service",
        "objects/create/Host",
        "objects/create/Service"
    ]
}

The last three objects/* permissions are only required if you want NSClient++ to auto-create missing host/service objects (see Step 5).


Step 1 — Verify the Icinga 2 Connection

Before configuring scheduled checks, verify that NSClient++ can submit a test result to the Icinga 2 REST API.

nscp client --module IcingaClient --query submit_icinga ^
    --address https://icinga2.example.com:5665/ ^
    --username nscp --password secret-password ^
    --command "service check" ^
    --result 2 ^
    --message "Hello from NSClient++"

Note

The ^ character is the Windows Command Prompt line-continuation character. In PowerShell use a backtick (`) instead, or write the command on a single line.

Expected output:

Successfully processed check result for object

In the Icinga 2 web UI you should immediately see the service service check on the host transition to CRITICAL with the message Hello from NSClient++. If you do not, double-check:

  • The ApiUser exists on the Icinga 2 server and has the actions/process-check-result permission.
  • A host with the matching name (the Windows hostname unless you override it — see Step 4) and a service called service check already exist on the Icinga 2 side, or ensure objects is enabled (Step 5).
  • Port 5665 is reachable from the Windows host.

TLS verify modes

Icinga 2's REST API ships with a self-signed certificate by default. Choose the verify mode that matches your setup:

verify mode value Behaviour When to use
none Accept any certificate 🚀 Quick start / lab
peer-cert Validate against the supplied ca file ✅ Production (recommended)
peer Validate but do not require a peer cert ⚠️ Rare

To validate the Icinga 2 self-signed certificate in production, copy the Icinga 2 CA (/var/lib/icinga2/ca/ca.crt on the Icinga 2 master) to the Windows host and reference it via the ca setting.


Step 2 — Configure the Scheduler

The Scheduler module runs your checks at regular intervals and sends results to a channel (ICINGA by default for IcingaClient).

Set default parameters for all scheduled jobs:

[/settings/scheduler/schedules/default]
channel = ICINGA  ; which channel to send results to
interval = 5m      ; how often to run each check
report = all     ; send results regardless of status (OK, WARN, CRIT)

Add scheduled checks:

[/settings/scheduler/schedules]
; Format: check_name = check_command [arguments...]
host_check = check_ok
cpu = check_cpu
memory = check_memory
disk_c = check_drivesize drive=C: "warn=free < 20%" "crit=free < 10%"

Each schedule key becomes the service name on the Icinga 2 side, except for the special name host_check — that one is submitted as a host check result rather than a service result. Use it for any check that should drive the host's UP/DOWN state in Icinga 2.

Note

The default schedule section applies its settings to all schedules that do not override them. You can override any setting per schedule by creating a dedicated section:

[/settings/scheduler/schedules/disk_c]
command  = check_drivesize drive=C: "warn=free < 20%" "crit=free < 10%"
interval = 15m   ; check disk less often than cpu/memory
channel  = ICINGA
report   = all

Step 3 — Configure the Icinga 2 Target

[/settings/Icinga/client/targets/default]
address = https://icinga2.example.com:5665/
username = nscp
password = secret-password
verify mode = peer-cert
ca = c:\program files\nsclient++\security\icinga2-ca.crt
Key Purpose
address Base URL of the Icinga 2 REST API. Defaults to port 5665 and protocol https.
username The Icinga 2 ApiUser name.
password The ApiUser password.
verify mode TLS verification — see the table in Step 1.
ca CA bundle used when verify mode = peer-cert.
tls version Optional TLS version pin (e.g. 1.2, 1.3).
timeout Per-request timeout in seconds (default 30).
proxy HTTP proxy URL — see the proxy notes below.
no proxy Comma-separated list of hosts that bypass the proxy.

Step 4 — Set the Hostname

NSClient++ must submit results using the hostname as it is known in Icinga 2. By default it uses the Windows computer name, which may not match.

Auto-detect hostname:

[/settings/Icinga/client]
hostname = auto

Use a specific hostname:

[/settings/Icinga/client]
hostname = win-server-01.example.com

Build the hostname from system variables:

[/settings/Icinga/client]
hostname = ${host_lc}.${domain_lc}

Supported variables:

Variable Meaning
${host} Windows computer name (mixed case)
${host_lc} Lowercase
${host_uc} Uppercase
${domain} Windows domain name
${domain_lc} Domain lowercase
${domain_uc} Domain uppercase

This name is used both as the Icinga 2 host name in the URL (?host=… / ?service=host!service) and as the check_source field on the resulting check result, unless you override check source on the target explicitly.


Step 5 — (Optional) Auto-create Host and Service Objects

By default, the host and service objects must already exist in the Icinga 2 configuration. If they do not, the REST API rejects the submission. Enable ensure objects to have NSClient++ create them on demand:

[/settings/Icinga/client/targets/default]
address = https://icinga2.example.com:5665/
username = nscp
password = secret-password
ensure objects = true
host template = generic-host
service template = generic-service
check command = dummy

When ensure objects = true, NSClient++ probes each host/service with a GET first and only PUTs a new object when the probe returns 404. Existing objects are left untouched. The ApiUser needs the objects/query/* and objects/create/* permissions listed in the Prerequisites.

Key Purpose
ensure objects When true, auto-create missing host/service objects before submitting.
host template Comma-separated Icinga 2 templates applied to auto-created hosts (default: generic-host).
service template Comma-separated Icinga 2 templates applied to auto-created services (default: generic-service).
check command The check_command to set on auto-created service objects (default: dummy).
check source Override for the check_source field reported to Icinga 2. Defaults to the local hostname when empty.

Warning

Auto-creating objects through the REST API does not persist them in your Icinga 2 configuration files — they are stored in Icinga 2's runtime config repository. If you reconfigure your monitoring through Director or text config, the auto-created objects may end up duplicated or removed. For production deployments most operators prefer to define objects explicitly and leave ensure objects = false.


Step 6 — Restart NSClient++

net stop nscp
net start nscp

After restart, NSClient++ will start executing scheduled checks and submitting results to Icinga 2. Your monitoring tool should begin receiving passive check results within one interval.


Complete Configuration Example

[/modules]
CheckSystem = enabled
CheckDisk = enabled
Scheduler = enabled
IcingaClient = enabled
CheckHelpers = enabled

[/settings/Icinga/client]
hostname = ${host_lc}.${domain_lc}

[/settings/Icinga/client/targets/default]
address = https://icinga2.example.com:5665/
username = nscp
password = s3cr3t_p@ssw0rd
verify mode = peer-cert
ca = c:\program files\nsclient++\security\icinga2-ca.crt
ensure objects = true

[/settings/scheduler/schedules/default]
channel = ICINGA
interval = 5m
report = all

[/settings/scheduler/schedules]
host_check = check_ok
cpu = check_cpu
memory = check_memory
disk_c = check_drivesize drive=C: "warn=free < 20%" "crit=free < 10%"

Using an HTTP Proxy

If the Windows host can only reach the Icinga 2 master through a corporate HTTP proxy, set proxy (and optionally no proxy) on the target. HTTPS is tunnelled to the master via an HTTP CONNECT request, so the same setting works for https:// Icinga 2 URLs.

[/settings/Icinga/client/targets/default]
address = https://icinga2.example.com:5665/
username = nscp
password = s3cr3t_p@ssw0rd
proxy = http://proxy.corp.example:3128/
no proxy = localhost,127.0.0.1,.internal

If the proxy itself needs credentials, embed them in the URL — @ and : inside the username/password must be percent-encoded:

proxy = http://alice:s%[email protected]:3128/

Monitoring Configuration on the Icinga 2 Side

For pre-defined services (no ensure objects), set them up as passive with a freshness threshold so Icinga 2 alerts you when results stop arriving:

apply Service "CPU Load" {
    import "generic-service"

    check_command          = "dummy"
    enable_active_checks   = false
    enable_passive_checks  = true
    check_freshness        = true
    freshness_threshold    = 600   /* seconds (10 minutes) */
    vars.dummy_state       = 3     /* UNKNOWN if no result */
    vars.dummy_text        = "No data received"

    assign where host.vars.os == "Windows"
}

Match freshness_threshold to your scheduler interval plus some headroom (e.g. interval = 5mfreshness_threshold = 600).


Next Steps