Distro support
Prometheus, node_exporter, and Grafana are portable (static binaries /
cross-platform packages); the systemd units are identical. The differences are
install source and opening the scrape port on the firewall. Body uses
Debian/Ubuntu; the RHEL family (Fedora, RHEL, CentOS Stream, Rocky, Alma,
Oracle) equivalents are in the matrix.
| Concept | Debian/Ubuntu | RHEL family |
|---|---|---|
| Install node_exporter | apt / release binary | dnf (EPEL has golang-github-prometheus-node-exporter) / release binary |
| Open scrape port (9100) | ufw allow 9100/tcp |
firewall-cmd --add-port=9100/tcp --permanent && firewall-cmd --reload |
| Install Telegraf | InfluxData apt repo / apt install telegraf |
InfluxData yum repo / dnf install telegraf |
| Install Datadog agent | official script / Datadog apt repo | official script / Datadog dnf repo |
| Open Telegraf scrape port (9273) | ufw allow 9273/tcp (restrict to monitor IP) |
firewall-cmd --add-rich-rule ... port=9273 |
| systemd unit / service | identical | identical |
| Log forwarding | rsyslog / journald | same (journald primary on RHEL) |
| Health endpoint pattern | identical | identical |
RHEL-family note: node_exporter is in EPEL on RHEL/Rocky/Alma
(ensure_epel), in main on Fedora, or install the upstream static binary on
either. Open the firewall with firewalld, not ufw.
In sk-* scripts use the common.sh primitives (pkg_install, ensure_epel,
firewall_allow) instead of hardcoding the family. See
linux-bash-scripting and
docs/multi-distro/plan.md.
Use when
- Adding metrics collection, log shipping, or health endpoints to a server.
- Making a host visible to Prometheus or a central logging pipeline.
- Standardizing
/healthbehavior for operational checks.
Do not use when
- The task is reading local logs only; use
linux-log-management. - The task is general performance triage without telemetry design work; use
linux-system-monitoring.
Required Inputs
| Artefact | Source | Required? | If absent |
|---|---|---|---|
| Telemetry objective and service | Service owner/SLO | yes | Stop before installing an unfocused collector |
| Collector or log destination contract | Platform owner | yes | Produce design only; do not expose a port or ship logs |
| Network, authentication, labels, retention | Platform/security policy | for integration | Mark end-to-end delivery unassessed |
| Change authority and rollback window | Operations owner | for mutation | Limit to read/search inspection and proposed config |
Capability Contract
Read/search access supports current-state assessment. Installing exporters/agents, editing units/config, opening ports, transmitting logs, or reloading services requires explicit authority. Use least-privilege credentials and never expose sensitive health data.
Degraded Mode
Fallback when receiver access is unavailable: validate only the reachable segment and label end-to-end scrape/delivery unassessed. Apply the same limit when network, credentials, root, or binaries are missing; a local listener is not proof of monitoring.
Decision Rules
| Choice | Action | Failure avoided |
|---|---|---|
| Prometheus can pull securely | Use scrape/exporter model | Unnecessary push state |
| Logs leave the host | Define filtering, TLS, backpressure, and retention | Data leak or disk exhaustion |
| Health dependency is slow/optional | Keep liveness cheap; separate readiness | Restart storms |
| Scrape port is required | Restrict source to collector | Public telemetry exposure |
Workflow
- Define the operational question, consumer, labels, retention, and access boundary.
- Inspect existing collectors, ports, units, and pipelines before adding components.
- Choose metrics, logs, or health semantics using the decision table; stop on undefined sensitive fields.
- Back up config, validate syntax, and apply the smallest authorised change.
- Verify from the consumer side, including auth, timestamps, labels, and failure state.
- On failure, recover prior config/service state and report which segment remains unassessed.
Evidence Produced
| Artefact | Acceptance |
|---|---|
| Consumer-side telemetry proof | Shows target, timestamp, expected metric/log/health value, and labels |
| Configuration evidence | Contains redacted diff, syntax check, service status, and rollback |
| Coverage statement | Names monitored components, failure states, retention, and unassessed segments |
Quality standards
- Observability changes must be testable from the receiving system, not only from the host.
- Keep health endpoints cheap, deterministic, and safe to expose.
- Prefer standard, maintainable telemetry patterns over bespoke one-offs.
Anti-patterns
- Declaring success from a local listener. Fix: verify scrape or receipt at the consumer.
- Shipping logs without destination/retention. Fix: define transport, filtering, backpressure, and expiry.
- Making
/healtha heavy diagnostic. Fix: keep liveness cheap and separate readiness/dependency detail. - Opening exporter ports globally. Fix: restrict sources to approved collectors.
- Putting secrets or high-cardinality values in labels. Fix: use bounded non-sensitive dimensions.
- Ignoring pipeline failure behaviour. Fix: test receiver outage, buffering, and recovery.
Outputs
| Artefact | Consumer | Acceptance condition |
|---|---|---|
| Telemetry integration | Operations | Consumer receives the intended signal with correct labels/timestamps |
| Operational runbook note | On-call | Names failure signal, retention, access, and recovery steps |
| Exposure record | Security owner | Documents listener, allowed sources, auth/TLS, and residual risk |
Worked Example
For node_exporter on Rocky Linux, restrict TCP 9100 to the Prometheus source, validate the unit, then prove the target is UP and node_uname_info carries the expected instance label. A successful local curl alone leaves consumer delivery unassessed.
References
-
../../docs/continuous-improvement/value-stream-5s-qc-story.md -
../../docs/continuous-improvement/incident-learning-standard.md
This skill is self-contained. Every step below uses standard tools and
released binaries; the body shows Debian/Ubuntu, with RHEL-family equivalents
in the Distro support matrix above. The sk-* scripts in the Optional
fast path section are convenience wrappers — never required.
This skill owns metrics and log shipping — turning a server from a black box you SSH into occasionally into a first-class citizen of a monitoring stack.
Complements linux-system-monitoring (local, interactive: top, htop,
iostat) with remote, continuous, aggregated observability (Prometheus
scrape targets, Loki log streams, standard /health endpoints).
Informed by Linux System Administration for the 2020s (observability as mandatory, not optional).
When to use
- Installing Prometheus
node_exporteron a server. - Creating a standard
/healthendpoint for a web service. - Forwarding logs from a server to a central collector (Loki, ELK, CloudWatch).
- Auditing which metrics and logs a server is currently emitting.
When NOT to use
top,htop,iostatfor right-now triage →linux-system-monitoring.- Grepping local log files →
linux-log-management. - Prometheus/Grafana alerting rules — those live in the monitoring server's config, not on the managed host.
Standing rules
- Every managed server exposes
node_exporteron127.0.0.1:9100. UFW allows scrape only from the monitoring server's IP — never from0.0.0.0. Exporter runs as its own unprivileged user. - Every web service has a
/healthendpoint that checks at minimum: database reachable, disk not full, required services running. 200 on healthy, 503 on unhealthy, JSON body with per-check status. - Logs ship off the server. Local files are for operator grep, not long-term storage.
- Metrics have cardinality discipline. No per-request unique IDs as labels. Ever. Prometheus explodes on high cardinality.
- Public vs internal health endpoints are split. Public
/healthreturns minimal info (200/503); internal/health/detail(behind firewall or auth) returns per-check JSON. - TLS on the wire for log shipping. Always. No plain-text logs crossing the network — they contain PII and tokens.
Quick reference — manual commands
Install node_exporter from GitHub release (canonical method)
# 1. Download and verify (check releases: https://github.com/prometheus/node_exporter)
VER=1.8.2
cd /tmp
curl -LO https://github.com/prometheus/node_exporter/releases/download/v${VER}/node_exporter-${VER}.linux-amd64.tar.gz
curl -LO https://github.com/prometheus/node_exporter/releases/download/v${VER}/sha256sums.txt
sha256sum -c sha256sums.txt --ignore-missing 2>&1 | grep OK
# 2. Install
tar xzf node_exporter-${VER}.linux-amd64.tar.gz
sudo install -m 0755 node_exporter-${VER}.linux-amd64/node_exporter /usr/local/bin/
# 3. Unprivileged user
sudo useradd --no-create-home --shell /usr/sbin/nologin node_exp
# 4. Systemd unit
sudo tee /etc/systemd/system/node_exporter.service >/dev/null <<'EOF'
[Unit]
Description=Prometheus node exporter
After=network-online.target
Wants=network-online.target
[Service]
User=node_exp
Group=node_exp
ExecStart=/usr/local/bin/node_exporter --web.listen-address=127.0.0.1:9100
Restart=on-failure
RestartSec=3
ProtectSystem=strict
ProtectHome=yes
NoNewPrivileges=yes
CapabilityBoundingSet=
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
sudo systemctl status node_exporter --no-pager
# 5. UFW — allow only from monitoring host
sudo ufw allow from 10.0.0.5 to any port 9100 proto tcp
# 6. Verify
curl -s localhost:9100/metrics | head -20
Full deep-dive (collectors, textfile collector, other exporters,
systemd hardening options, cardinality discipline) — see
references/prometheus-setup.md.
Other telemetry agents (Telegraf, Datadog) — alternatives to node_exporter
node_exporter is the recommended default: OSS, pull-based, minimal attack
surface, scraped by your own Prometheus. Two alternatives exist when you have a
concrete reason to use them:
| Agent | Model | Use when |
|---|---|---|
| node_exporter (default) | OSS, pull | You run (or want) your own Prometheus/Grafana stack — keeps the smallest surface. |
| Telegraf (InfluxData) | OSS, push | You already run InfluxDB, or want one plugin-rich agent (systemd/SNMP/MySQL inputs) pushing to InfluxDB or a Prometheus /metrics endpoint. Stays inside your network. |
| Datadog agent | SaaS, push | Your org pays for Datadog and wants metrics+logs+APM in one managed platform — and accepts that operational data (and logs, which carry PII/secrets) leaves your network to Datadog's cloud. |
Telegraf install (both families, signed repo), inputs
(cpu/mem/disk/net/systemd) + outputs (InfluxDB / Prometheus), Datadog install
via the official script/repo, datadog.yaml API-key config, enabling
integrations, and the privacy/egress consideration of a SaaS agent — all in
references/telemetry-agents.md.
API keys / tokens (Telegraf InfluxDB token, Datadog API key) are never put
in a world-readable config: store them in a 0600/root systemd env file and
inject via the unit — see linux-secrets.
Log forwarding with rsyslog (TLS)
sudo apt install rsyslog-gnutls
sudo mkdir -p /etc/rsyslog.d
# Minimal forwarder config (rsyslog.d/90-forward.conf)
sudo tee /etc/rsyslog.d/90-forward.conf >/dev/null <<'EOF'
$DefaultNetstreamDriver gtls
$DefaultNetstreamDriverCAFile /etc/ssl/certs/loghost-ca.crt
$ActionSendStreamDriverMode 1
$ActionSendStreamDriverAuthMode x509/name
$ActionSendStreamDriverPermittedPeer loghost.internal
$ActionQueueType LinkedList
$ActionQueueFileName fwdRule1
$ActionResumeRetryCount -1
$ActionQueueSaveOnShutdown on
*.* @@loghost.internal:6514
EOF
sudo systemctl restart rsyslog
Full setup with fluent-bit, vector, promtail, and journald forwarding —
see references/log-forwarding.md.
Minimal /health endpoint (Nginx → PHP)
# A /health.php file in your web root:
sudo tee /var/www/html/health.php >/dev/null <<'PHP'
<?php
header('Content-Type: application/json');
$checks = [];
$ok = true;
// DB reachable
try {
$pdo = new PDO('mysql:host=127.0.0.1;dbname=app', 'health', getenv('HEALTH_PASS'));
$pdo->query('SELECT 1');
$checks['db'] = 'ok';
} catch (Throwable $e) {
$checks['db'] = 'fail';
$ok = false;
}
// Disk < 90% on / and /var
foreach (['/', '/var'] as $mount) {
$pct = 100 - (disk_free_space($mount) / disk_total_space($mount) * 100);
$checks["disk:$mount"] = $pct < 90 ? 'ok' : 'fail';
if ($pct >= 90) $ok = false;
}
http_response_code($ok ? 200 : 503);
echo json_encode(['status' => $ok ? 'ok' : 'fail', 'checks' => $checks]);
PHP
Full pattern (Nginx-only health via stub_status, Node.js variant,
caching, auth for detail endpoint) — see
references/health-endpoint-pattern.md.
Typical workflows
Workflow: "Make this server visible to Prometheus"
- Install node_exporter (command sequence above).
sudo ufw allow from <monitor-ip> to any port 9100 proto tcp.curl -s localhost:9100/metrics | head -20— confirm metrics.- Add the server as a scrape target on your Prometheus server (outside the scope of this skill — that lives on the monitoring host).
Workflow: "Add /health to an existing vhost"
- Write the
health.php(or Node.js equivalent) above. - In the Nginx vhost, add:
location = /health { include fastcgi_params; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME /var/www/html/health.php; access_log off; } sudo nginx -t && sudo systemctl reload nginx.- Test:
curl -sI https://example.com/health→ 200 or 503.
Workflow: "Forward logs to a central Loki collector"
- Install fluent-bit (
apt install fluent-bitor upstream repo for newer versions). - Drop
/etc/fluent-bit/fluent-bit.conffromreferences/log-forwarding.md. sudo systemctl enable --now fluent-bit.- Verify arrival at the Loki server.
Troubleshooting / gotchas
node_exporterlistening on 0.0.0.0 by accident. The systemd unit above pins127.0.0.1:9100— verify withss -tlnp | grep 9100. If it's bound everywhere, fix the--web.listen-addressflag and reload.- UFW allows the scrape but Prometheus can't reach it. Check that
the rule is
from <monitoring-ip>notto <monitoring-ip>. Easy direction confusion. - High-cardinality metric explodes Prometheus. Signs: Prometheus OOMs after adding a new exporter. Usually a label with request IDs or user IDs. Drop the offending metric or relabel it away.
/healthpage cached by CDN. SetCache-Control: no-storein the Nginx location. A cached 200 will hide a real 503 from the load balancer for minutes.- rsyslog forwarding queues on disk fill /var/spool. Set a max queue
size on the
$ActionQueue*config, and monitor/var/spool/rsyslog. - Fluent-bit does not automatically rotate its own logs. Configure its log target carefully — it's a log shipper that can create logs.
References
references/prometheus-setup.md— full node_exporter install, other exporters, cardinality discipline, systemd hardening.references/telemetry-agents.md— Telegraf (InfluxData) and Datadog agent as alternatives to node_exporter: install on both families, Telegraf inputs/outputs (InfluxDB/Prometheus), Datadogdatadog.yaml/API key/integrations, "when to use which", and the privacy/egress consideration of a SaaS agent.references/log-forwarding.md— rsyslog/fluent-bit/vector/promtail with TLS forwarding examples.references/health-endpoint-pattern.md— full/healthpattern with PHP and Node.js examples, internal detail endpoint, caching strategy.- Book: Linux System Administration for the 2020s — observability is mandatory, not optional.
- Man pages:
rsyslogd(8),systemd.service(5).
Optional fast path (when sk-* scripts are installed)
Running sudo install-skills-bin linux-observability installs:
| Task | Fast-path script |
|---|---|
| Install node_exporter as unprivileged systemd service | sudo sk-node-exporter-install --monitor-ip <ip> |
| Install Telegraf (OSS alternative) with metrics inputs + an output | sudo sk-telegraf-setup --output prometheus |
Create/verify a /health endpoint for a vhost |
sudo sk-health-endpoint --domain <d> --db mysql |
| Configure log forwarding over TLS | sudo sk-log-forward-setup --collector <host>:<port> --tls |
These are optional wrappers around the manual steps above.
Scripts
This skill installs the following scripts to /usr/local/bin/. To install:
sudo install-skills-bin linux-observability
| Script | Source | Core? | Purpose |
|---|---|---|---|
| sk-node-exporter-install | scripts/sk-node-exporter-install.sh | no | Install Prometheus node_exporter as unprivileged systemd service, firewall-restrict, verify scrape. |
| sk-telegraf-setup | scripts/sk-telegraf-setup.sh | no | Install InfluxData Telegraf (OSS alternative to node_exporter) on both families from the signed repo; write host-metrics inputs (cpu/mem/disk/net/systemd) + one output (Prometheus /metrics or InfluxDB v2); firewall-restrict the scrape port; validate with telegraf --test. Token read from the 0600 env file, never the config. |
| sk-health-endpoint | scripts/sk-health-endpoint.sh | no | Create and verify /health for a vhost: checks db, disk, required services; 200/503 + JSON. |
| sk-log-forward-setup | scripts/sk-log-forward-setup.sh | no | Configure rsyslog or fluent-bit to forward journald and webserver logs to a central collector over TLS. |