Joseph Abbott III Software Engineer

Notes on software, projects, and education.


Project maintained by jabbott-iii Hosted on GitHub Pages — Theme by mattgraham

Salus v1.1.0 — Environment Health Checks from the Command Line

I am excited to announce Salus v1.1.0, the first feature release of my environment health checker!

Salus is a Go CLI that checks a host’s disk space and inodes, memory, CPU load, Docker and Kubernetes status, service uptime, failed systemd units, time synchronization, certificate expiry, and common misconfigurations, then prints a concise PASS / WARN / FAIL summary. It is built for operators, cron jobs, CI pipelines, and monitoring systems: the exit code reflects the worst result, and every run is recorded in a local SQLite database so you can review and compare runs later.

v1.0.0 shipped on September 27. v1.1.0 adds five new checks, monitoring-friendly output formats, run history tools, macOS and Windows support for the resource checks, and a multi-arch container image on GitHub Container Registry.

$ salus check run --only disk-space,memory,cpu-load,misconfig
Environment Health Check

[PASS] disk-space        /: 46.1% used (53.9% free)
[PASS] memory            memory 39.1% used, swap 8.9% used
[PASS] cpu-load          load average 1.80 across 16 CPU(s) (11% per-core)
[PASS] misconfig         no common misconfigurations detected

What Salus checks

Check What it verifies
disk-space Free disk space on one or more mount points
disk-inodes (new) Free inodes on one or more mount points
memory Memory and swap usage
cpu-load Load average relative to available CPUs (busy percentage on Windows)
docker-status The Docker daemon is reachable and no container is unhealthy or restarting
kubernetes-status The cluster is reachable and nodes are Ready without Memory, Disk, or PID pressure
kubernetes-pods (new) Pods in a namespace are Ready and not crash-looping or failed
service-uptime One or more systemd services are active (or host uptime when no service is given)
systemd-failed (new) No systemd unit is in the failed state
time-sync (new) The system clock is synchronized
cert-expiry (new) PEM or DER certificate files are valid and not close to expiry
misconfig Common environment misconfigurations

WARN and FAIL thresholds are set per run with --disk-warn/--disk-fail, --inode-warn/--inode-fail, --mem-warn/--mem-fail, and --load-warn/--load-fail. There is no configuration file to manage.

What’s new in v1.1.0

Example uses

# Scheduled host check from cron: alert on a non-zero exit code
salus check run --quiet

# CI or deployment gate: exit 2 (FAIL) is fatal, exit 1 (WARN) is a warning
salus check run --only disk-space,memory --disk-fail 95 --json

# Confirm a service is up after a deployment
salus check run --only service-uptime --service nginx

# Feed Prometheus through node_exporter's textfile collector, keeping 30 days of history
salus check run --format prometheus --output /var/lib/node_exporter/textfile/salus.prom --retain 30d

# Use Salus as a Nagios, Icinga, or Zabbix plugin
salus check run --format nagios --no-save

# Publish results as JUnit test cases in CI
salus check run --format junit --output salus-junit.xml --fail-on fail

# Warn two weeks before a certificate expires
salus check run --only cert-expiry --cert /etc/ssl/certs/site.pem --cert-warn-days 14

# After each scheduled run, exit 1 only if a result changed
salus jobs diff --exit-code

Exit codes

Code Meaning
0 Every check passed, or the command succeeded
1 At least one check reported WARN, and none reported FAIL
2 At least one check reported FAIL
3 Salus could not complete the command

These match the Nagios plugin convention, so Salus fits into existing monitoring without a wrapper script. Errors go to stderr, so --json output on stdout stays clean.

Installation

Download the archive for your platform and checksums.txt from the v1.1.0 release:

Platform Archive
Linux (x86-64) salus_linux_amd64.tar.gz
Linux (ARM64) salus_linux_arm64.tar.gz
macOS (Apple Silicon) salus_darwin_arm64.tar.gz
macOS (Intel) salus_darwin_amd64.tar.gz
Windows (x86-64) salus_windows_amd64.zip
Windows (ARM64) salus_windows_arm64.zip

On Linux (use linux_arm64 in place of linux_amd64 on ARM64):

grep salus_linux_amd64.tar.gz checksums.txt | sha256sum --check
tar -xzf salus_linux_amd64.tar.gz
sudo mv salus_linux_amd64 /usr/local/bin/salus
salus --version

The Linux binaries are statically linked, so they do not depend on the system’s C library. The macOS and Windows steps are in the README.

Container image

docker pull ghcr.io/jabbott-iii/salus:1.1.0
docker run -it --rm \
  -v salus-data:/app/data \
  -e SALUS_DB_PATH=/app/data/salus.db \
  ghcr.io/jabbott-iii/salus:1.1.0 check run

The image does not include docker, kubectl, systemctl, or timedatectl, so the Docker, Kubernetes, and systemd checks report WARN inside the container. Run the binary on the host for those checks, and do not mount the Docker socket into the container: it gives the container root-equivalent control of the host.

Building from source

Building needs Go 1.26.8 or newer and a C toolchain, because the SQLite driver uses CGO:

git clone https://github.com/jabbott-iii/Salus.git
cd Salus
make build
./salus --version

Verifying build provenance

Every release archive and the container image carry a signed SLSA build provenance attestation from the release workflow. Verify them with GitHub CLI 2.97 or newer (after gh auth login):

gh attestation verify salus_linux_amd64.tar.gz --repo jabbott-iii/Salus \
  --signer-workflow jabbott-iii/Salus/.github/workflows/cd.yml \
  --source-ref refs/tags/v1.1.0

gh attestation verify oci://ghcr.io/jabbott-iii/salus:1.1.0 --repo jabbott-iii/Salus \
  --signer-workflow jabbott-iii/Salus/.github/workflows/cd.yml \
  --source-ref refs/tags/v1.1.0

Upgrading from v1.0.2

Some of these changes can raise the exit code on hosts that passed before:

The full list is in the README under Upgrading from 1.0.2.

Security and quality

Get involved

Found a bug or have an idea for a check? Please open an issue. Contributions are welcome, see CONTRIBUTING.md.

Thank you for following along!

← Back to home