Zabbix output¶
The zabbix output makes the agent a native Zabbix active agent: it
connects out to a Zabbix server or proxy on port 10051, registers the host
through Zabbix autoregistration, asks which items the server wants for it,
and pushes their latest values in batches. Nothing listens on the agent
side; PRTG, Nagios and Prometheus keep working next to it.
It is proven against Zabbix 7.0 and 8.0 on a Linux and a Windows host:
every generated template imports into both lines, and encryption works
both ways with a certificate or a pre-shared key. Zabbix 6.0 LTS is
supported too: zabbix setup was measured on 6.0.48 and adapts to it on
its own (see Versions).
Deploying¶
Prepare the Zabbix server once with zabbix setup:
it imports the templates and creates the autoregistration actions. Then
give every agent the server's address when you install it; nothing else
is set, on the agent or in the Zabbix interface:
| Where | How |
|---|---|
| Windows | msiexec /i senhub-agent-<version>-amd64.msi /qn ZABBIX_SERVER=zabbix.example.com:10051 |
| Linux | after install: sudo /usr/local/bin/senhub-agent config init --zabbix-server zabbix.example.com:10051 |
| Container | SENHUB_ZABBIX_SERVER=zabbix.example.com:10051 |
The port is the server's or proxy's trapper port (ListenPort in its
configuration), 10051 by default; an address without a port gets 10051.
zabbix setup ends by printing these lines with the frontend's host and
no port, since it only talks to the frontend: add the port when the
server listens elsewhere, and replace the host when the server does not
run on the frontend's machine.
The agent asks the server for its items; the server does not know the
host and runs its autoregistration action, which creates it, puts it in
the SenHub group and links the templates of its platform. Discovery then
creates the items: on a Zabbix 8.0 server prepared by zabbix setup, a
new host appeared within a minute and had its items within five, with no
step on the Zabbix side. A machine already configured gets the output
the same way: config init adds the Zabbix file to an existing
configuration and the running agent picks it up.
Configuration¶
# strategies.d/20-zabbix.yaml
zabbix:
server: "zabbix.example.com:10051"
hostname: "web-01" # default: the machine's host name
host_metadata: "senhub-agent" # matched by the autoregistration action
interval: 60s # push cadence
refresh_interval: 120s # item list refresh
heartbeat_interval: 60s
timeout: 10s
key_prefix: senhub
tls:
enabled: false
ca_file: ""
cert_file: ""
key_file: ""
server_name: ""
insecure_skip_verify: false
| Parameter | Default | Description |
|---|---|---|
server |
required | Zabbix server or proxy, host:port; 10051 when the port is omitted. Several addresses separated by commas name a proxy group (see below). |
hostname |
machine host name | Name this host registers under. |
host_metadata |
senhub-agent |
Sent with every check-list request; the autoregistration action matches on it to choose host groups and templates. The agent appends its operating system, so senhub-agent linux, which is how the per-platform actions tell hosts apart. Limited to 2034 bytes by Zabbix. |
interval |
60s |
Push cadence of the collected values. Each push sends the latest value of every item, including the value of a probe that runs less often, until that probe's next run is due. |
refresh_interval |
120s |
How often the item list is asked again. |
heartbeat_interval |
60s |
Heartbeat cadence; the server declares the host unavailable after twice that. A server before 6.2 answers that it does not know the request; the agent then stops sending it. A heartbeat that fails because the connection failed is sent again at the next beat. |
timeout |
10s |
Bound on one connection, request and reply. |
key_prefix |
senhub |
First segment of every item key. |
passive.enabled |
false |
Answer the server's polls on the passive port (see below). |
passive.bind_address |
0.0.0.0 |
Address the passive listener binds to. |
passive.port |
10050 |
Port the passive listener binds to; sent to the server so autoregistration creates the agent interface on it. |
passive.allow |
server addresses | Addresses or CIDR ranges allowed to poll the passive port. |
passive.advertise |
source address | Address or name the server should poll, sent with the registration. |
passive.tls.enabled |
false |
Encrypt what the server polls. Needs cert_file and key_file, or psk_identity and psk_file. |
passive.tls.cert_file, passive.tls.key_file |
Certificate the agent presents to whoever polls it, and its key. | |
passive.tls.ca_file |
Authority that signed the server's certificate. When set, a poller must present one it signed. | |
tls.enabled |
false |
Encrypt the connection with TLS (certificate). |
tls.ca_file |
CA certificate that signed the server's certificate. | |
tls.cert_file, tls.key_file |
Client certificate and key, both or none. | |
tls.server_name |
server host | Name expected in the server's certificate. |
tls.insecure_skip_verify |
false |
Skip the server certificate check. |
tls.psk_identity |
Identity sent with the pre-shared key; must match what the server or the autoregistration setting holds. | |
tls.psk_file |
File holding the pre-shared key, hex-encoded as Zabbix writes it. Excludes cert_file. |
|
passive.tls.psk_identity, passive.tls.psk_file |
The same pair for the polled port, configured apart because the roles are opposite. |
A block takes a certificate or a pre-shared key, never both. See Encryption for both forms and for what each one buys.
Item keys¶
Every series is sent under a key built from the probe's definition:
The metric is the OTel name of the series (system.cpu.utilization,
senhub.veeam.job.status), so it is called the same thing here, on the
Prometheus endpoint and on the OTLP output. The dimensions are the
metric's multi_instance_labels, in the order the definition lists them;
a metric that names its own replaces the probe's rather than adding to
them, so a Windows drive metric carries the drive letter alone and not
the device and mount point it does not have.
The static attributes are the values of the metric's otel.attributes,
in attribute-key order: they tell apart the internal metrics that share
one OTel name, so on a probe named memory the used memory is
senhub.system.memory.usage[memory,used] and the free memory
senhub.system.memory.usage[memory,free], while a filesystem series
carries its device and mount point first:
senhub.system.filesystem.usage[logicaldisk,/dev/sda1,/,used]. A metric
whose OTel name already starts with the prefix is not prefixed twice. Values follow the OTel unit (a
percentage is a ratio, a duration is in seconds); an enum metric is sent
as its raw code under one key. The generated templates multiply a
utilization by 100 on the server side, so it is stored and shown as a
percentage (95.31 %) as the native agent shows it; write a trigger or
a calculated item against the percentage.
A metric that is a cumulative counter (network bytes and packets, disk
I/O, CPU time, request totals) is sent as its ever-growing total, which
a graph cannot read. The generated templates therefore add the
Change per second preprocessing step to its item, so the server
stores and shows a rate. The unit follows: bytes become Bps, a bare
count becomes /s, and any other unit gets /s appended (CPU time,
in seconds, is shown as s/s: the share of a second spent per second).
The key and what the agent sends do not change. Gauges, non-monotonic
sums (current connections) and the count and sum of a distribution are
stored as sent. Write a trigger or a calculated item against the rate.
The server only receives the keys it asked for. Until the host exists on the server and a template gives it items, the log says so at start and nothing is pushed.
Metrics relayed from another sender¶
An agent running the otlp_receiver probe relays what applications send
it, under the names they chose. The agent describes none of those names,
so their key is built from the metric name alone, and the emitter is
carried beside the probe:
where checkout is the sender's service.name. Without it two
applications reporting the same metric name through one receiver would
build the same key, and the second value would overwrite the first on an
item that goes on looking healthy. service.name is used rather than
the host name because a replaced container keeps the first and changes
the second, which would create an item on every deployment. A sender
that names itself neither keeps the shorter key.
The matching discovery rule is senhub.discovery[otlp_receiver,service.name],
whose instances carry {#PROBE} and {#SERVICE_NAME}.
The standard conventions arrive with a template¶
Zabbix asks for nothing it was not told about, so a relayed metric had to be declared by hand. The usual application metrics no longer do: the agent ships a template for the OpenTelemetry semantic conventions — HTTP server, JVM and database client — generated from the same definitions as every other template.
A host carrying it discovers the applications that relay through it, and their series become items keyed on the sender and on the attributes the convention defines:
senhub.http.server.request.duration.count[relay,checkout,GET,/cart,200]
senhub.jvm.memory.used[relay,checkout,heap,G1 Eden Space]
senhub.db.client.operation.duration.sum[relay,catalog,postgresql,SELECT]
Nothing is rewritten on the way. A relayed metric reaches every output through a pass-through that is consulted before any definition, so the name, the unit and the value stay the application's. The template only tells the server what to ask for.
A duration arrives as a distribution, not as a number: a count, a
sum and a bucket ladder. A sink that holds one value per item cannot
hold that, so the agent sends the two facts it can, under keys that say
which is which — .count and .sum. Sending the bare name would put a
number of requests under a key that reads as a latency. The average over
a period is the change in the sum divided by the change in the count,
which is a calculated item in Zabbix.
A metric outside those conventions is not lost. It keeps the shorter key its own name gives it, and an operator who wants it declares its item once. Adding a convention to the shipped set is a definition file, not code.
An application that exports only part of a set, one of the three JVM memory metrics of a pool for instance, gets items for what it sends and none for the rest: discovery lists, per sender, the metrics it feeds.
Templates and discovery¶
The templates are generated from the same definitions the keys come from, one per probe type:
senhub-agent zabbix template --out ./templates # every probe type
senhub-agent zabbix template --probe memory --probe veeam # a selection
senhub-agent zabbix template --probe veeam --version 6.0 # to standard output
Options: --version 6.0|7.0 (export format, 7.0 by default), --prefix
(must match the output's key_prefix), --delay (update interval of the
items, 1m by default), --out (directory; without it a single template
goes to standard output).
Every item of a template is a prototype under a low-level discovery rule,
because the probe instance name is discovered too: the rule
senhub.discovery[memory] returns
[{"{#PROBE}":"memory","{#PROBE_MEMORY}":"memory"}], and a metric with
dimensions hangs under the rule of its dimension set,
senhub.discovery[logicaldisk,device,mount_point], with one macro per
dimension.
Both macros carry the same instance name. The item keys use the one
named after the probe type, senhub.db.up[{#PROBE_POSTGRESQL}], because
Zabbix refuses to link two templates that declare the same key, and a
plain {#PROBE} made the PostgreSQL and MySQL templates both declare
senhub.db.up[{#PROBE}]. The key the agent sends is the same either
way, senhub.db.up[production-postgres]; item names keep {#PROBE}. The agent serves these discovery keys like any other item, so
a host gets its items within one discovery interval. The generated
templates set the discovery rules to 1 hour, and --delay does not
change it. zabbix setup sets the rules of the templates it imports to
5 minutes, so a new host fills within minutes; --discovery-delay
chooses another interval, and --no-discovery-delay leaves the rules at
the template's hour. An enum metric with a lookup gets a value map.
An instance only gets the items of the metrics it sends. Each discovery
row carries {#SENHUB.FED}, the list of metrics that instance feeds,
and the rule holds every prototype to it: a virtual network card whose
kernel reports no speed gets no speed item, while a bridge on the same
host that reports one does. An agent too old to send the list keeps
every item, as before.
Import the files through Data collection > Templates > Import, or
configuration.import on the API. Re-importing a regenerated template
updates the same objects: the identifiers are derived from the keys.
Every generated template is imported into a live Zabbix 7.0 and 8.0
before a release, in both export formats and for both platforms. Two
probes get no file at all: syslog and event relay records rather
than measuring anything, so they declare no item, and the command says
so on standard error rather than writing a template that could only
ever stay empty.
Two constraints of Zabbix's own are worth knowing if you write a
definition. A template's technical name is validated as a host name, so
the generator strips what that forbids — a friendly name like
"IBM i / Power Systems" becomes SenHub IBM i Power Systems, while the
name an operator reads keeps the slash. And the 6.0 export spells the
root template-group tag groups where 7.0 renamed it to
template_groups; --version picks the right one.
Triggers¶
The templates carry triggers, so a host linked to them raises problems without anyone writing an expression.
- States. A metric that reports a state (a virtual server that is down, a backup job that failed, a cluster in red) raises a problem from the same classification PRTG and Nagios read: the codes its lookup calls an error raise a High problem, the ones it calls a warning raise a Warning. The problem names the state, through the item's value map.
- Thresholds. Processor, memory and disk usage raise a Warning and a High problem when every value over the last five minutes is above a threshold: 80 and 90 % for the processor and the disks, 85 and 95 % for the memory, the values the shipped Nagios checks use. The warning depends on the high one, so a value past both raises one problem. Each threshold is a template macro, overridden on a host or a host group without editing the template (the table below also lists the clock tolerance, which is not a usage threshold):
| Macro | Default |
|---|---|
{$SENHUB.CPU_USAGE_TOTAL.WARN} / .CRIT} |
80 / 90 |
{$SENHUB.MEMORY_USED_PERCENT.WARN} / .CRIT} |
85 / 95 |
{$SENHUB.FS_USED_PERCENT.WARN} / .CRIT} (Linux) |
80 / 90 |
{$SENHUB.DISK_USED_PERCENT.WARN} / .CRIT} (Windows) |
80 / 90 |
{$SENHUB.CLOCK.DRIFT.MAX} |
60s |
- More thresholds. The processor queue or load, paging, disk queue
or busy time, inodes, interface errors and discards, and a recent
restart raise a Warning when every value over the last five minutes
is past the limit (the restart one while the uptime is under its
window). They are declared in the probe definitions under
triggers:and carry the macros below; a trigger over a metric a platform does not produce is left out of that platform's template.
| Macro | Default | Watches |
|---|---|---|
{$SENHUB.CPU_QUEUE.PER_CORE} |
3 | Processor queue length per logical processor (Windows) |
{$SENHUB.CPU_LOAD.PER_CORE} |
1.5 | 5 minute load average per logical processor (Linux) |
{$SENHUB.PAGING_USED_PERCENT.WARN} |
80 | Swap or pagefile usage, in percent |
{$SENHUB.DISK_QUEUE.MAX} |
5 | Disk queue length per drive (Windows) |
{$SENHUB.DISK_BUSY.MAX} |
0.9 | Fraction of time a device has I/O in flight (Linux) |
{$SENHUB.FS_INODES_USED_PERCENT.WARN} |
90 | Inode usage, in percent (Linux) |
{$SENHUB.NET_ERRORS.MAX} |
2 | Interface errors per second, either direction |
{$SENHUB.NET_DISCARDS.MAX} |
10 | Interface discards per second, either direction |
{$SENHUB.UPTIME.RESTART_WINDOW} |
10m | How long after a boot the restart problem stays open |
The host's uptime comes from system.uptime, sent by the cpu probe
(not a PRTG channel). The Windows services trigger and its macros are
below. No disk latency trigger exists: no probe
reports a latency yet, only throughput, operations, queue and busy time.
- Clock drift. The CPU template carries the host's clock as an item
shown as a date, and a Warning trigger,
fuzzytime(), that fires when the host's time is further from the server's than{$SENHUB.CLOCK.DRIFT.MAX}. It works the same on Zabbix 6.0 and 7.0.
Taken together, a Windows host linked to the generated templates is watched for processor, memory and paging use, disk space, disk queue, interface errors, services and restarts, each limit a macro.
Items and triggers are tagged the way the native templates are: every
item carries component (the probe type, or agent and inventory on
the agent's own items), and every trigger carries scope:
availability for a state, performance for the processor, capacity
for memory and disks. Filter problem views and actions on them.
Windows services¶
The winservices template discovers every service the agent reports,
one set of items per service name ({#WINDOWS_SERVICE_NAME}): whether it
runs, its Service Control Manager state, and its start type, each with a
value map. One trigger, Service ... is set to start automatically and
is not running (Average), fires when a service configured to start
automatically, delayed or not, has not been running for the whole grace
period. A manual or disabled service that is stopped raises nothing.
| Macro | Default | Effect |
|---|---|---|
{$SENHUB.WINSERVICES.GRACE} |
5m |
How long the service must have been stopped, which covers a restart and the delayed start after a boot |
{$SENHUB.WINSERVICES.MATCHES} |
.* |
A service is discovered only if its short name matches |
{$SENHUB.WINSERVICES.NOT_MATCHES} |
^(sppsvc\|clr_optimization_.*\|gupdate\|gupdatem\|edgeupdate\|edgeupdatem\|MapsBroker\|TrustedInstaller\|RemoteRegistry\|.*_[0-9a-f]{4,8})$ |
A service whose short name matches is not discovered: services Windows starts on demand, and the per-session copies whose name changes at every logon |
Override the macros on a host or a host group. The probe's services
parameter narrows what the agent reports at the source; the macros narrow
what the server creates.
Graphs and dashboards¶
The host probes' templates draw their metrics. A graph prototype under the discovery rule of the metrics it plots gives every discovered instance its own graph, named with the rule's macros:
| Template | Graphs (per instance) |
|---|---|
| CPU | utilization; load average (Linux); run queue (Windows); interrupts and context switches |
| Memory | utilization; swap or pagefile utilization; page faults; pages in and out (Windows) |
| Logical disk | filesystem or drive utilization (per mount point or drive); throughput and operations (per device or drive and direction); busy time (Linux); queue length (Windows) |
| Network | traffic, packets, errors and discards (per interface and direction) |
Each of these templates also carries a dashboard of its own graphs, two to a row, which Zabbix lists under the host's Dashboards menu, one entry per linked template. Zabbix resolves a dashboard widget against the graphs of the template that holds the dashboard and of no other, so there is no single combined page; the entries are the host overview.
The charts are declared in the probe definition, under graphs:, as a
name and the metrics to plot, by metric name:
graphs:
- name: "Disk throughput"
series: [diskio_read_bytes, diskio_write_bytes, disk_read_bytes_sec, disk_write_bytes_sec]
A metric the platform does not produce is left out, so one declaration serves Linux and Windows, and a chart with no series on a platform is not written. Two metrics of different discovery rules (a Linux and a Windows variant of the same fact) give one chart per rule. The 6.0 export carries the graphs but not the dashboards, whose widgets that format writes differently.
Encryption¶
The agent encrypts with certificates or with a pre-shared key, in both directions, which is the same choice a native Zabbix agent offers.
With a pre-shared key¶
zabbix:
server: "zabbix.example.com:10051"
tls:
enabled: true
psk_identity: "senhub-paris"
psk_file: /etc/senhub-agent/zabbix.psk
psk_file holds the key hex-encoded, exactly as Zabbix writes it, and
is the only place it appears: nothing carries it in the configuration,
so config show has nothing to redact and the file's permissions are
the protection. Generate one the way Zabbix documents:
openssl rand -hex 32 > /etc/senhub-agent/zabbix.psk
chmod 600 /etc/senhub-agent/zabbix.psk
chown senhub:senhub /etc/senhub-agent/zabbix.psk # the service account reads it
On the Zabbix side, set the same identity and key on the host, or in Administration > General > Autoregistration for agents that register themselves.
The polled port takes the same two settings under passive.tls, and the
two are configured apart because the roles are opposite. A block takes a
certificate or a pre-shared key, never both: Zabbix picks one
encryption per connection, and configuring both would leave the agent
choosing silently which secret proves it.
With a certificate¶
zabbix:
tls:
enabled: true
ca_file: /etc/senhub-agent/zabbix-ca.crt
cert_file: /etc/senhub-agent/agent.crt
key_file: /etc/senhub-agent/agent.key
See Encrypting the polled port for the listener's own block.
Which to choose¶
A pre-shared key needs no certificate authority, which is why most Zabbix sites use it, and it is the only encryption Zabbix offers for autoregistration — that setting takes "none", "PSK" or both, and refuses a certificate. So an agent that registers itself on a site which encrypts that step needs PSK. A certificate carries an identity a authority vouches for, and is the better answer where a PKI already exists.
What is implemented¶
TLS 1.2 with TLS_PSK_WITH_AES_128_GCM_SHA256 or
TLS_PSK_WITH_AES_256_GCM_SHA384, which is what Zabbix's own default
PSK cipher list offers. Go's standard TLS has no external pre-shared
keys, so this profile is implemented in the agent: one suite family, no
certificate, no resumption, no renegotiation, no 0-RTT.
Verified against real servers on both lines — the outbound connection,
autoregistration (the host is created with PSK set on both directions by
Zabbix itself), collection, and the polled port answering zabbix_get
--tls-connect psk. A wrong key, a wrong identity and an unencrypted
poll are all refused.
Not implemented: the TLS 1.3 external-PSK profile. Both Zabbix lines accept the 1.2 one, so this is not a limitation in practice; a server configured to refuse TLS 1.2 outright would not be served.
Passive polling¶
With passive.enabled: true the agent also listens on passive.port
(10050 by default) and answers the server's polls the way a classic
agent does: agent.ping, so the host's availability icon turns green,
agent.version, agent.hostname, and every item key the active push
sends, for an operator who prefers passive items. Both wire dialects are
served, the bare key of servers before 7.0 and the JSON batch of 7.0 and
later.
Only the addresses in passive.allow may poll; when the list is empty,
the addresses the configured server resolves to. The port is sent with
the registration request so the autoregistration action creates the
agent interface on it.
Zabbix records the address the packets came from, which behind NAT is
the translation and not somewhere it can poll. passive.advertise names
the address instead: a name creates a DNS interface, an address creates
an IP one.
Encrypting the polled port¶
The listener takes its own tls block, apart from the one on the
outbound connection, because the two are opposite roles: there the agent
checks a server, here it presents itself to one.
zabbix:
server: "zabbix.example.com:10051"
passive:
enabled: true
port: 10050
tls:
enabled: true
cert_file: /etc/senhub/agent.crt
key_file: /etc/senhub/agent.key
ca_file: /etc/senhub/zabbix-ca.crt
With cert_file and key_file alone, the agent encrypts what it serves
and anyone the allow list admits may read it. Add ca_file and the
agent also demands a certificate from whoever polls, signed by that
authority: the allow list says which addresses may connect, a
certificate says who they are.
On the Zabbix side, set the host's "Connections to host" to Certificate. A certificate that cannot be read stops the agent at start rather than leaving a listener that serves in clear.
The polled port also takes a pre-shared key, in place of the certificate:
zabbix:
server: "zabbix.example.com:10051"
passive:
enabled: true
port: 10050
tls:
enabled: true
psk_identity: "senhub-paris"
psk_file: /etc/senhub-agent/zabbix.psk
On the Zabbix side, set "Connections to host" to PSK with the same identity and key. A block takes a certificate or a pre-shared key, not both, and a key file that cannot be read or does not hold at least 16 bytes in hexadecimal stops the agent at start.
To restrict who may poll, without encryption:
zabbix:
server: "zabbix.example.com:10051"
passive:
enabled: true
port: 10050
allow: ["10.20.0.0/24"]
The host's inventory fills itself¶
Beside the measurements, the agent knows what the machine is: its operating system, its hardware, its serial number. Zabbix keeps those facts in host inventory, and the SenHub Agent template carries them as text items linked to the matching inventory fields.
| Inventory field | What the agent reports |
|---|---|
| Name | The name the machine reports for itself |
| OS, OS (Full), OS (Short) | Operating system, its full description, and its family |
| Type | What the machine is: a server, a virtual machine, a laptop |
| Hardware | Processor model |
| Vendor, Model | What the firmware names |
| Serial number A | Serial number from the firmware, which ties the host to an asset record |
zabbix setup sets the autoregistration action to put new hosts in
automatic inventory mode, without which the values arrive and fill
nothing. On a host created by hand, set the mode yourself under
Inventory.
A fact the agent did not find is not sent at all, so its field keeps whatever it held rather than being blanked. On one Linux host, eight of the nine fill by themselves at the first collection.
The relationships the agent also discovers, which machine a container runs on and which card is the same machine seen twice, have no home here: Zabbix has hosts, groups and tags, not a graph. They stay on the topology rail rather than being flattened into a text field.
One template set per platform¶
A definition declares every metric its probe can produce, and a probe does not produce the same ones everywhere: a processor's deferred procedure calls exist on Windows and nowhere else. Declaring them all on every host leaves items that can never receive a value, which an operator reads as a defect rather than as an absence.
So zabbix template writes one set per platform and names the files for
it, senhub-cpu-linux-7.0.yaml beside senhub-cpu-windows-7.0.yaml.
Use --platform linux or --platform windows; without it the template
carries every metric of the definition, which is what you want when you
generate for reading rather than for import.
Nothing has to be chosen per host. The agent appends its operating
system to the host metadata it registers with, and zabbix setup
creates one autoregistration action per platform matching on it, so a
Linux host is linked to the Linux templates and a Windows host to the
Windows ones by itself.
Which probes are linked without being named¶
A probe is linked by default when its definition marks it universal
and it has an item on the platform: the processor, memory, network,
disks and processes everywhere, and on Windows the services
(winservices) and the event log counter (windows_eventlog). The
other probes depend on what the host runs (hyperv on a Hyper-V host,
mssql beside an SQL Server, any vendor probe), so you name them with
--probe, or link everything with --all-probes. A template with
nothing to send on a platform, such as winservices on Linux, is not
imported for it.
windows_eventlog sends its records on the log rail, which the Zabbix
output does not carry, so what a Zabbix host gets from it is its
throughput counter, senhub.windows_eventlog.records_emitted, as a
rate. The records themselves go to an OTLP output.
Only the published platforms have an action. An agent built for macOS,
which is a development target and not a release, registers as
... darwin, matches nothing and waits for an autoregistration that
never comes; the server logs host [...] not found and the agent says
it is waiting. Add a condition for it by hand if you monitor one. A setup run on a server prepared by an earlier
version disables the single action that version created, because Zabbix
runs every matching action and leaving it would link both sets.
The agent's own items¶
Beside the probe templates, zabbix template writes a small SenHub
Agent template carrying agent.ping, agent.version and
agent.hostname. Link it on every host: agent.ping is what turns the
host's availability green, and the other two say which agent is running
there. zabbix setup imports it and adds it to the autoregistration
action by itself.
It is a template of its own because Zabbix refuses two linked templates that declare the same key, and every probe template is linked beside the others. The three items are served on both rails, so they arrive whether the host is monitored actively or polled on the passive port.
Through a proxy¶
Point server at the proxy instead of the server and nothing else
changes: the agent registers through it, the server attaches the host to
the proxy that relayed the registration, and the values travel the same
way.
A proxy group needs every member listed, separated by commas, the way
a classic agent takes several ServerActive entries:
The agent talks to the first member that answers. When the host is held by another member, that member replies with a redirection and the agent moves to it for every request, the check list, the values and the heartbeat alike. If the member holding the host goes down, the agent forgets the redirection and asks the configured addresses again, which is how it learns where the group moved the host.
With passive.enabled and no explicit passive.allow, every configured
address is allowed to poll the agent, because the member polling today is
not necessarily the one that polled yesterday.
Versions¶
Measured on a server of each line, not inferred from a changelog.
| Zabbix 7.0.30 | Zabbix 8.0.0 | |
|---|---|---|
zabbix setup (templates, host group, autoregistration action) |
yes | yes, unchanged |
| autoregistration | yes | yes |
| items by low-level discovery | yes | yes |
| host inventory filled from what the agent knows | yes | yes |
polled port read by zabbix_get |
yes | yes |
| proxy group redirection | yes | yes |
zabbix setup also prepares a 6.0 LTS server (measured on 6.0.48). A
server before 6.4 reads the API token only inside the request, and one
before 6.2 has no template groups and cannot read the 7.0 export format;
the command reads the server's version and adapts to both, so
--version 6.0 is chosen for you there.
Two things are worth knowing about the 8.0 line specifically.
The API no longer accepts the session token in the request body.
Where 7.0 took "auth": "<token>" inside the JSON-RPC envelope, 8.0
answers unexpected parameter "auth" and wants an
Authorization: Bearer header. senhub-agent zabbix setup reads the
server's version first and sends the header to 6.4 and later, the
auth field inside the request to older servers, so it works on every
line without a flag; a script of your own that drives the API may need
changing.
The proxy group redirection is unchanged. A host assigned to a group makes the server answer the check-list request with:
which is the same shape 7.0 emits, and the agent follows it to the
member holding the host. Two conditions have to be met for a group to
redirect at all, and missing either one looks like the feature not
working: the group needs a member that is online, and the host has
to be assigned with monitored_by set to the proxy group rather than
left on the server. While the assignment propagates, the server answers
once with host ... is monitored by a proxy and no redirect; the agent
logs it and the next request is redirected normally.
The templates are exported in the 7.0 format and import into 8.0 as they
are; --version 8.0 is not needed and does not exist.
zabbix setup options¶
senhub-agent zabbix setup --url <frontend> [--token-file <path>]
[--group <name>] [--metadata <string>]
[--action-name <name>] [--probe <type> ...] [--all-probes]
[--discovery-delay <interval> | --no-discovery-delay]
[--prefix <key prefix>] [--version 6.0|7.0] [--dry-run]
| Option | Default | Effect |
|---|---|---|
--url |
required | The Zabbix frontend, for example https://zabbix.example.com; /api_jsonrpc.php is appended |
--token-file, --token |
API token. Read from --token-file, then --token, then the environment variable SENHUB_ZABBIX_TOKEN. Prefer a file: a token on the command line is visible to every process on the machine |
|
--group |
SenHub Agents |
Host group new hosts are put in |
--metadata |
senhub-agent |
Host metadata the autoregistration action matches; must match the output's host_metadata |
--action-name |
Autoregistration — SenHub Agent |
Name of the autoregistration action |
--probe |
the probes every machine runs | Also import and link the template of this probe type; repeat for several. hyperv and mssql are linked this way, since only some hosts run them |
--all-probes |
Import and link every template that carries an item on the platform | |
--discovery-delay |
5m |
Interval set on the discovery rules of the imported templates |
--no-discovery-delay |
Leave the discovery rules at the template's interval (1 hour) | |
--prefix |
senhub |
Key prefix; must match the output's key_prefix |
--version |
7.0, or 6.0 on a server before 6.2 |
Template export format, 6.0 or 7.0 |
--dry-run |
Print what would be done without changing the server; without a token it describes the steps without reading the server |
Run it once, as a Zabbix administrator; a deployed agent never holds an API token. Every step is idempotent, so re-running it is safe and is how templates are refreshed after an upgrade.
Autoregistration¶
Create an action under Alerts > Actions > Autoregistration actions
with a condition on the host metadata (contains senhub-agent, or
whatever you set in host_metadata) and three operations: add host, add
to a host group, link the generated templates. Every agent whose
metadata matches then appears by itself at its first check-list request,
with its items created by discovery within the discovery interval.
zabbix setup creates these actions for you, one per platform.
A host already registered keeps the templates it was given¶
Zabbix runs an autoregistration action when a host registers, not
afterwards. Re-running zabbix setup after an upgrade refreshes the
content of every template, and hosts already linked to them receive the
change. A template the action links for the first time, because you
named a new probe with --probe, reaches only the hosts that register
from then on. For the hosts already there, link it yourself: select them
under Data collection > Hosts, then Mass update > Templates >
Link.