---
module: agent.srv-monitor
title: Server Monitor — CPU/memory/disk/service health watcher for SRV devices
tab: Internal
order: 20
audience: dev
---

> **Internal · ops/support view.** Confirmed against `hiveops-module-srv-monitor` source (`com.hiveops.srvmonitor`) 2026-07-01. HiveIQ agent drop-in module. Not customer-facing.

## What it does

The **Server Monitor** module (`srv-monitor`) turns a HiveIQ agent install into a lightweight server-health probe. On a fixed interval it samples four things and reports **only state transitions** (breach and recovery) to the platform as journal events on an `SRV`-type device:

| Signal | What it watches | Breach event | Recovery event |
|---|---|---|---|
| CPU | busy % (sustained N samples) | `SERVER_CPU_HIGH` | `SERVER_CPU_NORMAL` |
| Memory | used % of physical RAM | `SERVER_MEMORY_HIGH` | `SERVER_MEMORY_NORMAL` |
| Disk | used % per filesystem/volume | `SERVER_DISK_LOW` | `SERVER_DISK_NORMAL` |
| Services | named services up/down | `SERVICE_DOWN` | `SERVICE_UP` |

It is edge-triggered: it fires an event when a metric crosses its threshold and again when it drops back, not on every poll. This keeps the incident stream quiet — one HIGH and one NORMAL per episode, not one per minute.

CPU has a debounce: it must stay at/above threshold for `srv.monitor.cpu.sustained.samples` consecutive polls before `SERVER_CPU_HIGH` fires (default 3). Memory, disk, and service transitions fire immediately on the first breaching poll.

The module is designed for the planned **SRV device type** (branch back-office / QDS servers, not ATMs) — events carry `deviceType=SRV` and `eventSource=HIVEOPS_AGENT`, so the device auto-registers as an SRV device on first event, the same way ATMs auto-register on first journal event.

## Config properties it registers

All read from the agent's main `hiveops.properties` in `initialize()`. Defaults are the code defaults:

| Property | Default | Meaning |
|---|---|---|
| `srv.monitor.enabled` | `false` | Master gate. Module stays inactive unless this is `true`. |
| `srv.monitor.interval.seconds` | `60` | Poll interval (fixed-rate). Also the initial delay is 0 — first poll on start. |
| `srv.monitor.cpu.threshold` | `85` | CPU busy-% threshold. |
| `srv.monitor.cpu.sustained.samples` | `3` | Consecutive breaching polls required before `SERVER_CPU_HIGH`. |
| `srv.monitor.memory.threshold` | `90` | Memory used-% threshold. |
| `srv.monitor.disk.threshold` | `90` | Per-filesystem used-% threshold. |
| `srv.monitor.services` | *(empty)* | Comma-separated service names to watch (e.g. `hiveops-agent,qds,postgresql`). Empty = no service checks. |

It also **reuses** the shared agent endpoint/auth config (not srv-monitor-specific):

- `agent.endpoint` (preferred) or `incident.endpoint` (fallback) — the HTTP base URL events are POSTed to. If neither is set the module logs `no endpoint configured, module inactive` and stays down even when enabled.
- The matching HTTP client settings (`agent.*` or `incident.*` — auth token, institution key, timeouts) are loaded via the same `HttpClientHelper` path every other event-emitting module uses.

## How it's enabled / deployed to ATMs

Two independent switches must both be right: the **JAR must be present** and `srv.monitor.enabled=true`.

1. **Ship the JAR.** `srv-monitor` is a drop-in module JAR (`hiveops-module-srv-monitor.jar`), not bundled in the core agent. It lives in the agent's `modules/` directory. The agent's `ModuleLoader` discovers it there on startup.
2. **Fleet delivery — `INSTALL_MODULE` task.** Push the JAR fleet-wide with an `INSTALL_MODULE` fleet task. The agent's `ProcessInstallModule` handler downloads it (either from a fleet artifact via chunked download, or from a `configPatch.url`/`filename` pair — filename must end in `.jar`), drops it into `modules/`, then restarts the agent so the new module loads. See the [technical.agent] fleet-task docs for the task shape.
3. **Turn it on.** Set `srv.monitor.enabled=true` (plus any threshold/service overrides) in `hiveops.properties`, typically via a `CONFIG_UPDATE` fleet task. The module reads config only at `initialize()`, so a config change needs an agent restart to take effect.
4. **Disable** either by `srv.monitor.enabled=false` or by adding `srv-monitor` to the `modules.disabled` comma list.

## Failure modes / what to check

- **Enabled but no events ever.** Check `agent.endpoint`/`incident.endpoint` is set — with neither, the module logs `no endpoint configured, module inactive` and never schedules. Also confirm the JAR actually landed in `modules/` and the agent restarted after `INSTALL_MODULE`.
- **No CPU events despite obvious load.** CPU is debounced by `srv.monitor.cpu.sustained.samples` (default 3) — a spike shorter than 3 poll intervals never fires. At the 60 s default that's a ~3-minute sustained requirement.
- **No disk events for a full `/var` (Linux).** The Linux collector enumerates `File.listRoots()`, which on Linux is typically just `/`. Separate mount points that aren't reported as roots are **not** monitored per-partition. Windows reports each logical drive (`C:`, `D:` …) individually.
- **`SERVICE_DOWN` for a service that's actually up.** Linux uses `systemctl is-active <name>` (expects exactly `active`); Windows uses `Get-Service -Name <name>` (expects `Running`). A wrong unit name, a non-systemd Linux host, or a service the query can't reach is reported **down**. Verify the exact service/unit name in `srv.monitor.services`.
- **Nothing on Windows + PowerShell warnings in the log.** The Windows collector shells out to `powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass`. If PowerShell is locked down or missing, CPU/mem/disk read back as `0.0`/empty and a non-zero exit is logged with stderr.
- **Event stream noisy / flapping.** A metric hovering right at the threshold will alternate HIGH/NORMAL each poll (memory/disk/service have no debounce). Raise the threshold or the interval.
- **Where to look:** the module logs under its own package `com.hiveops.srvmonitor` → `logs/modules/srv-monitor.log`. Startup line `srv-monitor initialized (interval=…, cpu=…%, …)` confirms it armed with the config you expect.
