Appearance
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:
- 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. - The current working directory.
- The agent's state directory, usually
~/.config/daemonitor. Getting started lists the full order. /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, webRestart 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.
| Key | What it does |
|---|---|
plugins | Array of plugin names to run. A plugin not named here does not run, even if it has a block below. |
connectors | Where 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:
| Name | Watches |
|---|---|
os | CPU, memory, disk, network, uptime |
pm2 | Processes managed by pm2 |
docker | Docker containers, one report per compose project |
lxc | LXC, LXD or Incus containers |
web | HTTP checks against URLs |
wordpress | WordPress security posture through WP-CLI |
logwatch | Web access logs for attack patterns |
netscan | Ping reachability of LAN devices |
snmp | SNMP devices such as printers and NAS units |
tplink | TP-Link extenders and access points |
3cx | A 3CX phone system |
patroni | A Patroni Postgres node. Needed for cluster leader tracking |
adsense | Google AdSense earnings |
ga4 | Google 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.
| Field | Meaning |
|---|---|
name | Display name for the plugin's row in the dashboard. |
uniqueId | The 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. |
refreshInterval | Milliseconds between reports. |
Defaults when refreshInterval is absent:
| Plugin | Default |
|---|---|
pm2 | 5000 |
os, patroni, netscan | 30000 |
docker, lxc, web, logwatch, 3cx | 60000 |
tplink | 120000 |
wordpress, snmp | 300000 |
adsense, ga4 | 900000 |
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.
| Type | What it does |
|---|---|
rest-api | Sends each report to the server with PUT /api/clientstate/update. This is the one you want. |
console | Prints 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.
| Flag | Variable | Meaning |
|---|---|---|
--token <token> | DAEMONITOR_TOKEN | Enrollment token. Only used when no key is stored yet. |
--key <key> | DAEMONITOR_KEY, or SYSTEM_KEY | System key. Skips enrollment. |
--endpoint <url> | DAEMONITOR_ENDPOINT, or API_BASE_URL | API base URL. Default https://daemonitor.com/api. |
--config <path> | Path to client.config.json. | |
--dir <path> | DAEMONITOR_DIR | State directory for credentials.json and the config. |
--plugins <list> | Comma-separated plugin list. Replaces the plugins array from the file. | |
--install-service | Install and start the systemd unit, then exit. Needs root. | |
-h, --help | Print usage. | |
-v, --version | Print 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:
--keyorDAEMONITOR_KEY.credentials.jsonfrom an earlier enrollment.- 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.