Skip to content

Configuration ​

The agent reads one JSON file, client.config.json. It says which plugins to run, how to configure each one, and where to send the results. Enrollment writes a working file for you. This page covers the file when you want to change it.

Where the file lives ​

The agent looks for client.config.json in this order and uses the first one that parses:

  1. The path given with --config. If you name a file and it can't be read, the agent exits instead of falling back to defaults.
  2. The current working directory.
  3. The agent's state directory, usually ~/.config/daemonitor. Getting started lists the full order.
  4. /etc/daemonitor/client.config.json.

A file that exists but isn't valid JSON is skipped with a warning. If nothing is found, the agent runs with { "plugins": ["os"] }.

The startup banner prints which file it loaded:

daemonitor-client 1.2.1 on web-01
  endpoint  https://daemonitor.com/api
  config    /home/deploy/.config/daemonitor/client.config.json
  plugins   os, docker, web

Restart the agent after editing the file. It does not watch for changes.

Structure ​

json
{
  "connectors": ["rest-api"],
  "plugins": ["os", "docker", "web"],

  "os": {
    "name": "os",
    "uniqueId": "os",
    "refreshInterval": 30000
  },
  "docker": {
    "name": "docker",
    "uniqueId": "docker",
    "refreshInterval": 60000,
    "dockerBin": "docker",
    "projects": [],
    "exclude": []
  },
  "web": {
    "name": "web",
    "uniqueId": "web",
    "refreshInterval": 60000,
    "endpoints": [
      {
        "name": "Marketing site",
        "url": "https://www.example.com/",
        "expectedStrings": ["Example Domain"],
        "expectedStatusCode": 200,
        "timeout": 5000
      }
    ]
  }
}

There are three kinds of top-level key.

KeyWhat it does
pluginsArray of plugin names to run. A plugin not named here does not run, even if it has a block below.
connectorsWhere reports go. Optional. Defaults to rest-api.
<plugin name>One object per plugin, keyed by the same name used in plugins.

The system key and endpoint do not belong in this file. The agent takes them from credentials.json, a flag or an environment variable, and passes them to the rest-api connector itself.

The plugins array ​

Names are matched case-insensitively. The agent in @daemonitor/client 1.2.x loads these plugins:

NameWatches
osCPU, memory, disk, network, uptime
pm2Processes managed by pm2
dockerDocker containers, one report per compose project
lxcLXC, LXD or Incus containers
webHTTP checks against URLs
wordpressWordPress security posture through WP-CLI
logwatchWeb access logs for attack patterns
netscanPing reachability of LAN devices
snmpSNMP devices such as printers and NAS units
tplinkTP-Link extenders and access points
3cxA 3CX phone system
patroniA Patroni Postgres node. Needed for cluster leader tracking
adsenseGoogle AdSense earnings
ga4Google Analytics 4 properties

When enrollment builds your config and you picked no plugins, it uses what the agent found on the machine. It enables pm2 if the binary is on PATH, docker if the socket is readable by the user the service will run as, lxc if an LXD socket exists, and always os.

Per-plugin blocks ​

Every plugin reads the block named after it. Three fields are common to all of them.

FieldMeaning
nameDisplay name for the plugin's row in the dashboard.
uniqueIdThe identity of the row on the server. Reports with the same uniqueId update the same service. Defaults to <name>-<n>, where n depends on plugin load order, so set it explicitly. Cluster rules match on it.
refreshIntervalMilliseconds between reports.

Defaults when refreshInterval is absent:

PluginDefault
pm25000
os, patroni, netscan30000
docker, lxc, web, logwatch, 3cx60000
tplink120000
wordpress, snmp300000
adsense, ga4900000

Don't set an interval longer than the dashboard's staleness window. A service that hasn't reported for 5 minutes shows as offline. wordpress gets 15 minutes and adsense and ga4 get 30. See How status works.

The plugin-specific fields, such as endpoints for web or sites for wordpress, are documented on each plugin's own page.

Web checks managed from the dashboard ​

Web checks can live in the dashboard under Settings, Web monitors, instead of in this file. The web plugin fetches them when its block has both of these fields:

json
"web": {
  "uniqueId": "web",
  "refreshInterval": 60000,
  "configUrl": "https://daemonitor.com/api/monitors/for-client",
  "systemKey": "<this system's key>",
  "endpoints": []
}

The plugin merges the two lists by name and URL. A check defined in the dashboard wins over a local one with the same name and URL. If a fetch fails, the plugin keeps running the last list it fetched.

Connectors ​

A connector decides where reports go. Two exist.

TypeWhat it does
rest-apiSends each report to the server with PUT /api/clientstate/update. This is the one you want.
consolePrints a line per report to stdout. Set "verbose": true in its config to print the full payload. Useful for testing a plugin without sending anything.

The short form is an array of names:

json
"connectors": ["rest-api", "console"]

The long form takes a config object:

json
"connectors": [
  { "type": "rest-api", "name": "REST API", "config": {} },
  { "type": "console", "config": { "verbose": true } }
]

rest-api always inherits the endpoint and system key the agent resolved at startup, so its config can stay empty. A value you set in its config, such as apiUrl or systemKey, overrides the inherited one.

Each push has a 15 second timeout. A failed push is logged and dropped. The next interval sends a fresh report, so nothing queues up.

Command-line flags and environment variables ​

Every flag has an environment variable. A flag wins over the variable.

FlagVariableMeaning
--token <token>DAEMONITOR_TOKENEnrollment token. Only used when no key is stored yet.
--key <key>DAEMONITOR_KEY, or SYSTEM_KEYSystem key. Skips enrollment.
--endpoint <url>DAEMONITOR_ENDPOINT, or API_BASE_URLAPI base URL. Default https://daemonitor.com/api.
--config <path>Path to client.config.json.
--dir <path>DAEMONITOR_DIRState directory for credentials.json and the config.
--plugins <list>Comma-separated plugin list. Replaces the plugins array from the file.
--install-serviceInstall and start the systemd unit, then exit. Needs root.
-h, --helpPrint usage.
-v, --versionPrint the agent version.

The agent also loads a .env file from its working directory, so these variables can live there.

Where the agent gets its key, in order:

  1. --key or DAEMONITOR_KEY.
  2. credentials.json from an earlier enrollment.
  3. Enrollment with --token.

The endpoint follows the same idea. With no --endpoint, the agent uses the endpoint stored in credentials.json, so an agent enrolled against a self-hosted server keeps talking to it.

A minimal file for a web server ​

json
{
  "plugins": ["os", "web"],
  "os": { "name": "os", "uniqueId": "os" },
  "web": {
    "name": "web",
    "uniqueId": "web",
    "endpoints": [
      { "name": "Shop", "url": "https://shop.example.com/", "expectedStatusCode": 200 },
      { "name": "API health", "url": "https://api.example.com/health", "expectedStrings": ["ok"] }
    ]
  }
}

Run daemonitor-client once in the foreground and check that the banner lists both plugins, then restart the service.