Hosts and containers
Servers and containers from node_exporter, cAdvisor and their logs, with built-in alarms.
Monitor → Infrastructure shows the servers and containers behind your endpoints. It is built from data you may already collect:
| Data | Comes from | Gives |
|---|---|---|
| Host metrics | node_exporter | CPU, memory, disks, load, network, uptime of each machine |
| Container metrics | cAdvisor | CPU and memory against limits, restarts, out-of-memory kills, network of each container |
| Container logs | Fluent Bit (Logs → Sources) | The log lines next to each container's charts, and the Errors in container logs alarm |
Nothing new has to run on a machine that is already monitored. Pulsimo either scrapes the exporters itself, or receives a copy from the Prometheus (or agent) that scrapes them today.
Connecting#
Click Add hosts (top right), or use the Connect tab. There are three ways in, and they can be mixed:
| Tab of Add hosts | Use it when | What Pulsimo does |
|---|---|---|
| New server | A server with Docker that nothing monitors yet | Gives one command that starts a small agent |
| Exporters already running | node_exporter, cAdvisor or other exporters run and Pulsimo can reach them | Scrapes them |
| Prometheus or agent | A Prometheus or agent already scrapes them, or Pulsimo can't reach the machines | Receives a copy (remote_write) |
| Logs already shipped | Promtail, Alloy, Vector, Filebeat, Logstash, Fluentd or the OTel Collector already ship logs | Becomes one more output |
New server (one command)#
Add hosts → New server. For a server with Docker (and Docker Compose) that nothing monitors yet.
- Type the server's name, for example
web-1. It becomes thehostlabel: it names the host page and ties the server's containers and logs to it. - Prepare the command. Pulsimo creates two sources for the server, web-1 agent (metrics) and web-1 logs (logs), and shows one block to paste on the server. The tokens are inside and are shown only this once.
- Paste it on the server as a user that can run Docker. It writes four files to
~/pulsimo-agentand runsdocker compose up -d.
The checklist next to the command updates every 5 seconds:
| Step | Done when |
|---|---|
| The agent connects | vmagent's first metrics arrive |
| Host metrics | node_exporter reports the machine |
| Containers | cAdvisor reports the containers |
| Container logs | Fluent Bit's first lines arrive (they come in batches, up to 30 s) |
When all four are ticked, Open web-1 goes to the host page. You can close the window before that; the server shows up under Hosts as soon as it reports.
What runs on the server, all with network_mode: host and restart: unless-stopped:
| Container | Does | Listens on |
|---|---|---|
pulsimo-agent-node-exporter (node_exporter 1.8.2) |
The machine: CPU, memory, disks, network | 127.0.0.1:9100 |
pulsimo-agent-cadvisor (cAdvisor 0.60.6) |
The containers; Compose labels only, per-CPU and network-protocol metrics off to keep it light | 127.0.0.1:18080 |
pulsimo-agent-vmagent (vmagent 1.106.1) |
Scrapes both every 15 s and pushes to Pulsimo; keeps up to 1 GB on disk while Pulsimo is unreachable | 127.0.0.1:8429 |
pulsimo-agent-fluent-bit (Fluent Bit) |
Reads the log files Docker already writes and pushes them | nothing |
Nothing is reachable from outside the server, and your containers are not changed.
The address the agent sends to is LOG_INGEST_PUBLIC_URL (Configuration), else the address
your browser uses. When that is localhost, the screen warns that the command only works on
the Pulsimo machine itself: set LOG_INGEST_PUBLIC_URL and prepare it again.
Preparing the same server again keeps its two sources and gives them new tokens, so an agent started with the earlier command stops sending until you run the new one.
To remove the agent: cd ~/pulsimo-agent && docker compose down -v, then delete web-1
agent (Infrastructure → Connect) and web-1 logs (Logs → Sources).
1. Prometheus or agent (recommended)#
Use this when node_exporter and cAdvisor are already scraped by Prometheus, kube-prometheus-stack, Grafana Alloy, vmagent or the OpenTelemetry Collector, or when the machines can't be reached from Pulsimo.
- Click Connect Prometheus, give the connection a name (Production Prometheus) and,
if you like, extra labels such as
env=productionorcluster=eu-1. They are added to every series the connection sends. - Pulsimo shows the token once, the remote_write URL and a ready-made snippet for Prometheus, kube-prometheus-stack, Grafana Alloy, vmagent and the OpenTelemetry Collector. Copy the token now: Pulsimo keeps only a hash of it.
- Add the snippet to your Prometheus or agent and reload it.
Your Prometheus keeps working as before and also sends a copy to Pulsimo. Hosts and containers appear within a minute. Under Prometheus connections each connection shows when it last pushed, how many writes and how much data it sent. The dot turns green once the first write arrives.
Each connection has three buttons:
- Rotate token makes a new token and stops the old one; update the snippet on your side afterwards.
- Pause refuses its writes, so what it sends meanwhile is lost; Resume accepts them again.
- Delete removes the connection and its token. The data it already sent stays until the metrics retention ends.
A change takes effect within a minute. Every series a connection sends is tagged with your organization and the connection, and those tags cannot be overwritten by the sender. Connections are not listed under Metrics → Sources (that page lists what Pulsimo scrapes); it links here instead.
The address in the snippets is LOG_INGEST_PUBLIC_URL when set (Configuration), otherwise the
address your browser uses. Set it when your servers reach Pulsimo through a different name.
Agents on every machine. When an agent on each server scrapes
localhost:9100and they all push through one connection, give each agent ahostlabel (for exampleexternal_labels: { host: web-1 }). Pulsimo uses it to keep the machines apart.
2. Exporters already running#
Add hosts → Exporters already running. Paste the targets, or click Load a file:
- a list, one per line, with optional labels after it:
10.0.1.12:9100 env=prod; - a Prometheus
file_sdfile, JSON or YAML; - the
scrape_configsof aprometheus.yml: everystatic_configstarget is read, with its labels and the job'sschemeandmetrics_path.
An address without a path gets /metrics, and without a scheme http://. Up to 300
targets at a time. Labels starting with __ and the ones Pulsimo sets itself (instance,
job, source, org_id…) are dropped.
Check targets scrapes each one once and shows:
| Column | Meaning |
|---|---|
| Target | The URL, and whether it answered (with its sample count and operating system) or why not |
| Type | What it serves: node_exporter (it has node_uname_info), cAdvisor, PostgreSQL (pg_up), MySQL (mysql_up), an application (HTTP request metrics) or other. Change it if Pulsimo guessed wrong |
| Host label | Puts a machine's exporters on one host page. For node_exporter it is the machine name it reports; other exporters at the same address get the same one. Edit it freely |
| Name | The source's name, unique in your organization (Pulsimo adds a number when needed) |
Targets that are already a source are greyed out. Tick the ones to keep (those that answered are ticked), pick how often to scrape (15, 30 or 60 seconds) and click Add N sources. Targets that did not answer can be kept too: Pulsimo keeps trying and shows the error under Metrics → Sources.
The machines must be reachable from Pulsimo. When they are behind a firewall or in another network, use the Prometheus or agent tab: it pushes, so no port has to be opened.
You can still add a single exporter under Metrics → Sources, with authentication and certificate options.
3. Logs already shipped#
Add hosts → Logs already shipped (or Logs → Sources → Setup → Another shipper). Create a log source, or pick one you have, and Pulsimo shows the output to add to your shipper. Your current destination keeps getting everything.
| Shipper | Protocol | Address |
|---|---|---|
| Promtail, Grafana Alloy | Loki push (protobuf or JSON) | /api/v1/logs/loki/api/v1/push |
| Filebeat, Logstash | Elasticsearch bulk | /api/v1/logs/elasticsearch |
| OpenTelemetry Collector | OTLP/HTTP logs (protobuf or JSON) | /api/v1/logs/otlp/v1/logs |
| Vector, Fluentd, Fluent Bit, anything else | JSON lines | /api/v1/logs/ingest |
The token goes in as a Bearer token, or as the password of basic auth (Filebeat and
Logstash: user pulsimo). Lines from every protocol get the same treatment as Fluent Bit's:
the service, container, host, namespace and pod are read from the names each tool uses
(Loki labels such as service_name and container, ECS fields such as service.name and
host.name, OpenTelemetry attributes such as service.name, k8s.pod.name), the level is
detected, JSON messages are lifted into fields, redaction and the line-rate limit apply.
For Filebeat and Logstash, turn off index templates and lifecycle management (the snippet does): Pulsimo answers like Elasticsearch 8 but stores lines its own way. Filebeat has a single output, so either point it at Pulsimo or run a second Filebeat.
Exporter versions#
- node_exporter 1.6 or newer. Run it with the host's network and root file system so it
sees the machine and not its own container (
--net=host --pid=host -v /:/host:roand--path.rootfs=/host). - cAdvisor from
ghcr.io/google/cadvisor, recent. Version 0.52 and older cannot read containers on Docker 29 with the containerd image store ("failed to identify the read-write layer ID"): containers are missing or have no name.
The page#
The cards on top:
| Card | Meaning | Click |
|---|---|---|
| Hosts | Hosts reporting / all seen in the last 24 hours | Hosts tab |
| Containers | Running / all seen in the last 24 hours | Containers tab |
| Restarts, 24 h | Restarts of all containers, and how many containers restarted | Containers that need a look |
| Out of memory, 24 h | Containers killed by the kernel for going over their memory limit | Containers that need a look |
| Open alarms | Host and container alarms firing now | Alerting |
| Sources | Scraped exporters and Prometheus connections | Connect tab |
A host is Up when its metrics are less than 2 minutes old. A container is Running when cAdvisor saw it in the last 2 minutes; Stopped containers stay listed for a day.
Hosts#
Each host with CPU, memory and its fullest disk as bars (amber from 80%, red from 90%), load (amber when above the number of cores), network, its containers (running / all), how long it has been up, and its open alarms. Down hosts and hosts with alarms come first. Click a host to open it.
Containers#
Each container with its image and Compose project, its host, CPU (with the limit when it has one), memory against its limit, network, restarts and out-of-memory kills in 24 hours, uptime and alarms. Filter by text, host, Compose project and All / Running / Stopped / Needs a look (stopped, restarted, killed, alarm, or memory at 90% of its limit or more).
Stopped containers that never ran 10 minutes and were never restarted (one-off
docker run --rm commands, CI jobs) are left out, and the Container stopped alarm skips
them too. A service that crashes right after a redeploy still counts, because an earlier
container with its name ran longer, and so does one that crash-looped until it gave up.
A restart is the same container starting again (a crash, docker restart, a restart
policy). Recreating a container, as docker compose up does after a change, is a new
container and is not counted as a restart.
A host#
Infrastructure → a host. Pick 1 hour, 6 hours, 24 hours or 7 days.
- Cards: CPU, memory, fullest disk, load (and its share of the cores), network, uptime.
- CPU split into user, system and waiting for disk.
- Memory used, and cache and buffers, against the total.
- Load over 1, 5 and 15 minutes, with a line at the number of cores.
- Network received and sent (virtual interfaces of Docker and Kubernetes left out).
- Disk activity: bytes read and written.
- Disks: every file system, fullest first (tmpfs, overlay and similar left out).
- Open alarms of the host.
- Containers on it, with the same table and filters as the Containers tab.
- Container events: restarts, out-of-memory kills and new images.
- Logs of its containers, with Warnings and errors to narrow them.
A container#
Infrastructure → a container. The charts and the logs sit side by side, so a spike and the lines written at that moment are in view together.
- Cards: CPU (cores, and the share of its limit), memory (and the share of its limit), restarts and out-of-memory kills in 24 hours, network, uptime.
- CPU and Memory with a red line at the limit, and Network.
- Dashed markers on every chart: amber for a restart, red for an out-of-memory kill, violet for a new image.
- Logs of the container (up to the last 24 hours), with Warnings and errors and a link to the Log Browser.
- Open alarms and Events.
- Details: host, Compose project and service, limits, the cAdvisor that reports it. A container without a memory limit gets a note: it can use all of the host's memory and the near-the-limit alarm cannot watch it.
Built-in alarms#
Every organization has these rules, on by default. They appear under Alerting → Rules → Built in, where you can change their thresholds, severity, how long the problem must last, or turn them off. They cannot be created a second time as custom rules.
| Rule | Fires when | Default | Closes by itself |
|---|---|---|---|
| Host unreachable | No metrics from a host | 120 s, critical | when its metrics arrive again |
| Disk filling up | A file system is nearly full | 90% for 5 min, critical at 95% | when it drops below |
| Host memory pressure | Memory used (not counting cache) | 90% for 10 min, critical at 97% | when it drops below |
| Host CPU saturated | CPU busy | 90% for 15 min | when it drops below |
| Container restarting | Restarts in a short while (crash loop) | 2 in 15 min, critical at 5 | when it stops restarting for the window |
| Container out of memory | The kernel killed it for its memory limit | any kill in 15 min, critical | when no kill happened in the window |
| Container near memory limit | Memory used against its limit | 90% for 5 min, critical at 98% | when it drops below |
| Container stopped | A container that once ran 10 minutes or more, or was restarted, in the last day stopped | 120 s | when it runs again |
| Errors in container logs | Error lines from one container | 20 in 5 min, critical at 100 | when the burst stops |
Alarms carry the source Infrastructure in Alerting and are routed, acknowledged and muted like every other alarm (Alarms and rules). A host or container that is removed for good keeps its unreachable or gone alarm until it has not been seen for 24 hours; resolve it by hand if you removed it on purpose.
What else uses this data#
- Probable causes of an incident: an out-of-memory kill, a restart or a new image of a container linked to the endpoint shortly before it failed is listed first.
- Changes: container restarts, out-of-memory kills and new images are recorded as changes, shown on the charts of linked endpoints.
- Digest: containers using far more CPU (3× and at least a quarter of a core) or memory (2× and at least 256 MB) than at the same time yesterday or last week.
Kubernetes#
Clusters, workloads and pods have their own tab and pages: see Kubernetes.