Read-only scan
Kernel & bootloader validation, boot-partition health, SELinux/AppArmor, failed logins, journal errors, memory pressure. Nothing is changed unless you opt in.
vitals_scanLinuxVitals is an agentless Ansible collection that scans, opt-in self-heals, and reports on your Linux servers — with per-distro reboot and kernel detection, a baseline → postcheck comparison built for real maintenance windows, and one self-contained HTML dashboard at the end of it.
ansible-galaxy collection install sameeralam3127.linux_vitals
Six things LinuxVitals does out of the box, across RHEL, Fedora, Ubuntu, and SUSE.
Kernel & bootloader validation, boot-partition health, SELinux/AppArmor, failed logins, journal errors, memory pressure. Nothing is changed unless you opt in.
vitals_scanOne restart attempt per enabled, failed systemd service — and the report says whether it worked. Disabled by default (linux_vitals_heal_enabled: false); nothing changes unless you flip the switch.
Snapshot before a maintenance window, snapshot after — correlated by a maintenance id. Status changes, RAM deltas, kernel changes, and new/resolved findings, computed for you.
vitals_reportOne HTML file. No CDN, no build step, no server. Search, sort, filter, and expandable host rows — opens in any browser, forever, offline.
dashboard.html.j2Slack, email, and generic webhook — configurable independently, with secrets resolved from inventory, group_vars, or a local .env file.
Every push boots a real systemd container per family and runs the whole pipeline end to end — discovery, healing, and reporting — not just a lint pass.
moleculeReal template, sample fleet data. Search a hostname, click a column to sort, expand a row for the full picture — including a live before/after maintenance comparison.
So LinuxVitals asks each one in its own language, reports which source produced the answer, and never reads a broken check as "all clear" — an unusable tool falls back to a kernel comparison and says so.
| Family | Primary check | Reported as | Packages |
|---|---|---|---|
| Debian / Ubuntu | /run/reboot-required |
reboot-required-file |
apt |
| RHEL / Rocky / Alma | needs-restarting -r |
needs-restarting |
dnf |
| Fedora | dnf needs-restarting |
dnf-needs-restarting |
dnf5 |
| openSUSE / SLES | zypper needs-rebooting |
zypper-needs-rebooting |
zypper |
| No usable check | running vs. latest installed kernel | kernel-comparison |
— |
The same care goes into the bootloader: GRUB via grubby, grubenv, BLS drop-in
entries and systemd-boot are all resolved, so a host with the newest kernel installed but an older one
set to boot gets flagged before it reboots into a surprise.
Read the detection docs ↗
Three playbooks, one shared maintenance id.
Run …linux_vitals.baseline with a linux_vitals_maintenance_id before your window opens. Full posture, snapshotted per host.
Patch, reboot, reconfigure — however you normally do it. LinuxVitals stays out of the way; it only observes and, if you've enabled it, self-heals.
Run …linux_vitals.postcheck with the same id. The dashboard shows exactly what changed — per host, per finding.
No agents, no daemons, no database — just SSH and Ansible. Python 3.10+ and Ansible Core 2.16+ on the control node.
# install the collection ansible-galaxy collection install sameeralam3127.linux_vitals # and its runtime dependency ansible-galaxy collection install community.general
collections: - name: sameeralam3127.linux_vitals version: ">=1.2.0" - name: community.general
# one-shot health check across the fleet ansible-playbook -i inventory.ini sameeralam3127.linux_vitals.healthcheck # before a maintenance window ansible-playbook -i inventory.ini sameeralam3127.linux_vitals.baseline \ -e linux_vitals_maintenance_id=2026-08-patch # after it, for the before/after diff ansible-playbook -i inventory.ini sameeralam3127.linux_vitals.postcheck \ -e linux_vitals_maintenance_id=2026-08-patch
Every option below is generated from the collection's own meta/argument_specs.yml — all 51 variables across the four roles, with their real defaults. Change what you need; only what differs from a default ends up in the output.
Report paths and the .env lookup resolve from this file's directory, not from the playbook's.
Ansible's default is 5. Raising this is the cheapest fleet-scale win; match it to control-node cores and to what your bastion tolerates.
Needed to read /var/log/btmp, the journal, and grub.cfg. Without it those checks degrade quietly. Set it in the inventory rather than with --become, which would also try to escalate on the control node during reporting.
Leave all off to run everything. Include reporting with any focused tag, or the run produces no output file.
Pulled live from the open issues on GitHub. Nothing here is a commitment or a shipping date — track progress and weigh in.
Expose the JSON report as scrapeable metrics for long-term trend dashboards in Grafana.
ExploringClassify findings by severity and let alert thresholds key off them, so a database primary and a scratch box are not treated alike.
ExploringA vitals_certs role for certificate expiry and hardening posture, on disk and over the network.
Install it, point it at an inventory, and open one HTML file.