This Django app provides a small health-check framework and two management commands you can run locally or in automation.
See Architecture for how this app fits in the pipeline (CHECK stage).
CheckRun— Log of standalone health check executions- Status, message, and metrics from the check
- Link to created Alert (if check found an issue)
- Execution timing and correlation ID for tracing
Note: When checks run as part of the pipeline, tracking is handled by
apps.orchestrationviaStageExecutionwithstage="check".
These checkers are registered in apps/checkers/checkers/__init__.py (CHECKER_REGISTRY):
cpu— CPU usage % (warn ≥ 70, critical ≥ 90)memory— RAM usage % (warn ≥ 70, critical ≥ 90)disk— disk usage % by path (warn ≥ 80, critical ≥ 95)disk_macos— macOS disk analysis: space hogs, old files, cleanup recommendations (warn ≥ 5 GB, critical ≥ 20 GB recoverable). Skips on non-darwin.disk_linux— Linux disk analysis: apt cache, journal logs, Docker/Snap data, old temp files (warn ≥ 5 GB, critical ≥ 20 GB recoverable). Skips on non-linux.disk_common— Cross-platform disk analysis: system logs, user caches, temp files, large files in home (warn ≥ 5 GB, critical ≥ 20 GB recoverable).disk_inodes— Inode usage per filesystem viaos.statvfs(cross-platform Unix — Linux + macOS; no sudo). Catches inode exhaustion (many tiny files) that free-space checks miss. Worst path drives status: warn ≥ 80 %, critical ≥ 95 %. Filesystems that don't track inodes (f_files == 0) are skipped; skips as OK wherestatvfsis unavailable (e.g. Windows).network— % of hosts reachable via ping (OK ≥ 70%, warning ≥ 50%, else critical)process— % of named processes running (OK = 100%, warning ≥ 50%, else critical)raid— Linux software-RAID (mdadm) health from/proc/mdstat(no sudo, no subprocess). Degraded-and-not-rebuilding or inactive array → CRITICAL; actively rebuilding (recovery/resync/reshape) → WARNING; routinecheck/repairscrubs and healthy arrays → OK. Skips as OK on non-linux or when/proc/mdstatis absent.disk_temp— Hottest disk temperature via Linux hwmon (psutil.sensors_temperatures(), chipsdrivetemp/nvme; no sudo). Worst disk drives status: warn ≥ 55 °C, critical ≥ 60 °C. Skips as OK on non-linux or when no disk sensors are present. Requires thedrivetempkernel module (Linux 5.6+) loaded (modprobe drivetemp) for SATA/NVMe temps without root; SMART/smartctlis out of scope.cpu_temp— Hottest CPU package/core temperature via Linux hwmon (psutil.sensors_temperatures(), chipscoretemp/k10temp/zenpower/cpu_thermal; no sudo). Warm sensor drives status: warn ≥ 80 °C, critical ≥ 90 °C. Skips as OK on non-linux or when no CPU sensors are present.io_strain— Busiest disk IO utilization (% busy time) viapsutil.disk_io_counters(perdisk=True), samplingbusy_timeover an interval (default 1 s; no sudo). Busiest disk drives status: warn ≥ 80 %, critical ≥ 95 %. Metrics also include per-disk throughput (MB/s) and IOPS. Skips as OK on non-linux or when no IO counters /busy_timeare available.listening_ports— Audits the host's LISTENing ports viapsutil.net_connections(reads/proc/net; no sudo on Linux). Ports outsidesettings.LISTENING_PORTS_ALLOWLISTare flagged WARNING; with no allowlist set, only externally-exposed (non-loopback) ports are flagged. Configure via theLISTENING_PORTS_ALLOWLISTenv var (comma-separated ports). Linux-gated — skips as OK on non-linux (wherenet_connectionsneeds root) or if the sockets can't be read.
List them via:
uv run python manage.py check_health --listWhich checkers run is controlled by pipeline definitions. The context node's checker_names config specifies which checkers to include:
{"id": "check_health", "type": "context", "config": {"checker_names": ["cpu", "memory", "disk"]}}If checker_names is omitted, all registered checkers run.
You can also specify checkers explicitly via the CLI:
uv run python manage.py run_pipeline --checks-only --checkers cpu memory diskAccess the admin interface at /admin/checkers/ to view check run history:
- CheckRun — View all check executions (read-only)
- Colored status badges (ok/warning/critical/unknown)
- Duration display
- Link to created alert (if any)
- Filter by status, checker name, hostname
- Search by checker name, hostname, message, trace ID
- Date hierarchy by execution time
Note: Check runs are audit records and cannot be added/edited manually. They are created automatically when checks are run via management commands or the orchestration pipeline.
In addition to runtime health checkers, this app registers Django system checks that run with python manage.py check. These verify project configuration:
| Tag | Check ID | Description |
|---|---|---|
database |
checkers.E001 |
Database connection error |
migrations |
checkers.W001 |
Pending migrations warning |
security |
checkers.W010 |
DEBUG mode enabled |
security |
checkers.W011 |
Weak SECRET_KEY |
environment |
checkers.W012 |
.env file missing |
environment |
checkers.I003 |
Required env var not set (from .env.sample, info) |
environment |
checkers.W017 |
Project directory not writable |
pipeline |
checkers.I001 |
Pipeline definition counts (info) |
pipeline |
checkers.W014 |
No active notification channels or empty config |
crontab |
checkers.W002 |
No crontab configured |
crontab |
checkers.W004 |
Health check cron job not found |
crontab |
checkers.W015 |
cron.log stale (> 1 hour) |
crontab |
checkers.W016 |
cron.log too large (> 50MB) |
database |
checkers.E003 |
Missing django_migrations table (deploy) |
Run all system checks:
uv run python manage.py checkRun specific check tags:
uv run python manage.py check --tag database
uv run python manage.py check --tag migrations
uv run python manage.py check --tag security
uv run python manage.py check --tag environment
uv run python manage.py check --tag pipeline
uv run python manage.py check --tag crontabRun deployment checks (includes additional security/config checks):
uv run python manage.py check --deployThe preflight command runs all Django system checks grouped by tag with formatted output:
# Run all checks
uv run python manage.py preflight
# Filter by tag(s)
uv run python manage.py preflight --only security
uv run python manage.py preflight --only security,environment
# JSON output (for CI)
uv run python manage.py preflight --jsonFor a comprehensive check that includes shell-level pre-Django checks (Python version, uv installed, disk space) plus all Django preflight checks:
bin/check_system.sh # Full check (shell + Django)
bin/check_system.sh --shell-only # Only shell-level checks
bin/check_system.sh --django-only # Only Django preflightThere are two management commands for running checks. All flags can be passed after aliases too (e.g., sm-check-health --json).
Run all checkers (or a selection) and show a summary.
# Run ALL registered checkers
uv run python manage.py check_health
# Run specific checkers only
uv run python manage.py check_health cpu memory
uv run python manage.py check_health cpu memory disk network process
# List available checkers and exit
uv run python manage.py check_health --list# JSON output (for scripts, cron, piping to jq)
uv run python manage.py check_health --json
# Specific checkers + JSON
uv run python manage.py check_health cpu disk --jsonBy default: exit 2 if any CRITICAL, 1 if any UNKNOWN, 0 otherwise.
# Exit 1 if ANY check is WARNING or CRITICAL (strictest)
uv run python manage.py check_health --fail-on-warning
# Exit 1 only if ANY check is CRITICAL
uv run python manage.py check_health --fail-on-critical
# CI pipeline example: fail build on critical
uv run python manage.py check_health --fail-on-critical --jsonOverride default warning/critical thresholds for all checkers in this run:
# Lower thresholds (more sensitive)
uv run python manage.py check_health --warning-threshold 60 --critical-threshold 80
# Higher thresholds (less sensitive)
uv run python manage.py check_health --warning-threshold 85 --critical-threshold 98
# Override thresholds for specific checkers only
uv run python manage.py check_health cpu memory --warning-threshold 75 --critical-threshold 95These flags are passed to the relevant checker when it runs:
# Disk: check specific mount points
uv run python manage.py check_health disk --disk-paths / /var /tmp /home
# Network: ping specific hosts
uv run python manage.py check_health network --ping-hosts 8.8.8.8 1.1.1.1 github.com
# Process: verify specific processes are running
uv run python manage.py check_health process --processes nginx gunicorn postgres# Full CI check: all checkers, JSON, fail on warning
uv run python manage.py check_health --json --fail-on-warning
# Disk + network with custom targets + thresholds
uv run python manage.py check_health disk network \
--disk-paths / /var/log \
--ping-hosts 8.8.8.8 google.com \
--warning-threshold 75 --critical-threshold 90
# Cron job: all checks, JSON, append to log
uv run python manage.py check_health --json >> /var/log/health-checks.log 2>&1
# Quick smoke test: CPU + memory, fail on critical
uv run python manage.py check_health cpu memory --fail-on-critical| Flag | Type | Default | Description |
|---|---|---|---|
checkers (positional) |
str... | all | Specific checkers to run (space-separated) |
--list |
flag | — | List available checkers and exit |
--json |
flag | — | Output results as JSON |
--fail-on-warning |
flag | — | Exit 1 if any WARNING or CRITICAL |
--fail-on-critical |
flag | — | Exit 1 only if any CRITICAL |
--warning-threshold |
float | per-checker | Override warning threshold for all checks |
--critical-threshold |
float | per-checker | Override critical threshold for all checks |
--disk-paths |
str... | / |
Paths to check (disk checker) |
--ping-hosts |
str... | 8.8.8.8 1.1.1.1 |
Hosts to ping (network checker) |
--processes |
str... | — | Process names to check (process checker) |
Run a single checker with checker-specific options.
# Basic usage
uv run python manage.py run_check cpu
uv run python manage.py run_check memory
uv run python manage.py run_check disk
uv run python manage.py run_check network
uv run python manage.py run_check processuv run python manage.py run_check cpu --json
uv run python manage.py run_check disk --json# Override thresholds for this single check
uv run python manage.py run_check cpu --warning-threshold 80 --critical-threshold 95
uv run python manage.py run_check memory --warning-threshold 75 --critical-threshold 90
uv run python manage.py run_check disk --warning-threshold 85 --critical-threshold 98# Default: 5 samples, 1 second apart
uv run python manage.py run_check cpu
# More samples for better accuracy
uv run python manage.py run_check cpu --samples 10
# Faster sampling (0.5s intervals)
uv run python manage.py run_check cpu --sample-interval 0.5
# Quick snapshot (1 sample, no wait)
uv run python manage.py run_check cpu --samples 1 --sample-interval 0
# Per-CPU mode (reports busiest core)
uv run python manage.py run_check cpu --per-cpu
# All CPU options combined
uv run python manage.py run_check cpu --samples 10 --sample-interval 0.5 --per-cpu
# CPU with threshold override + JSON
uv run python manage.py run_check cpu --samples 10 --per-cpu --warning-threshold 80 --critical-threshold 95 --json# Default: RAM only
uv run python manage.py run_check memory
# Include swap memory in the check
uv run python manage.py run_check memory --include-swap
# Memory with custom thresholds
uv run python manage.py run_check memory --include-swap --warning-threshold 75 --critical-threshold 90 --json# Default: check /
uv run python manage.py run_check disk
# Check specific paths
uv run python manage.py run_check disk --paths /
uv run python manage.py run_check disk --paths / /var /tmp /home
uv run python manage.py run_check disk --paths /var/log /var/lib
# Disk with thresholds + JSON
uv run python manage.py run_check disk --paths / /var/log --warning-threshold 80 --critical-threshold 95 --json# Default hosts: 8.8.8.8, 1.1.1.1
uv run python manage.py run_check network
# Custom hosts
uv run python manage.py run_check network --hosts 8.8.8.8 1.1.1.1 github.com
uv run python manage.py run_check network --hosts google.com cloudflare.com aws.amazon.com
# Network with JSON
uv run python manage.py run_check network --hosts 8.8.8.8 google.com --json# Check specific processes
uv run python manage.py run_check process --names nginx
uv run python manage.py run_check process --names nginx postgres gunicorn
uv run python manage.py run_check process --names nginx gunicorn postgres
# Process with JSON
uv run python manage.py run_check process --names nginx postgres --json| Flag | Type | Default | Description |
|---|---|---|---|
checker (positional) |
str | required | Checker name (cpu, memory, disk, network, process, etc.) |
--json |
flag | — | Output as JSON |
--warning-threshold |
float | per-checker | Override warning threshold |
--critical-threshold |
float | per-checker | Override critical threshold |
--samples |
int | 5 | Number of CPU samples (cpu only) |
--sample-interval |
float | 1.0 | Seconds between CPU samples (cpu only) |
--per-cpu |
flag | — | Per-CPU mode, reports busiest core (cpu only) |
--include-swap |
flag | — | Include swap memory (memory only) |
--paths |
str... | / |
Disk paths to check (disk only) |
--hosts |
str... | 8.8.8.8 1.1.1.1 |
Hosts to ping (network only) |
--names |
str... | — | Process names to check (process only) |
Checkers live in:
apps/checkers/checkers/
To add a new checker:
- Inherit from
BaseChecker - Return a
CheckResult - Register it in
CHECKER_REGISTRY
The network checker uses the system ping binary. In some environments (containers, locked-down CI), ping may be blocked.
Workarounds:
- run without the
networkchecker, or - adjust platform/network permissions
If a disk path doesn’t exist, the disk checker returns UNKNOWN for that path.