sunnysahijwani / redis-health-digest
Zero-dependency Redis health & cache-performance digest for Laravel — thresholded OK/WARN/CRITICAL status, hit-rate, memory and keyspace metrics, ready for a daily cron email.
Package info
github.com/sunnysahijwani/redis-health-digest
pkg:composer/sunnysahijwani/redis-health-digest
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/contracts: ^10.0|^11.0|^12.0|^13.0
- illuminate/mail: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- pestphp/pest: ^2.0|^3.0|^4.0
This package is auto-updated.
Last update: 2026-08-12 11:35:25 UTC
README
A tiny, dependency-light Laravel package that samples a Redis instance with a single read-only INFO command, evaluates the result against configurable thresholds, and emits a thresholded OK / WARN / CRITICAL digest — as an email, as JSON, or as a console table.
Point it at a cron once a day and you get a health report in your inbox. Wire the same command into your alerting and it exits non-zero when Redis is unhealthy.
Runs one
INFOcall per sample. No keys are written, noSELECTis issued, nothing is flushed — safe to run against production.
Why
Most teams discover Redis problems — a collapsing hit rate, creeping memory, a failed background save, connections being rejected — only when something downstream breaks. This package turns those signals into a boring daily email and a non-zero exit code, so a human (or an alert) notices first.
It reports the metrics that actually predict trouble:
- Cache performance — lifetime hits/misses and hit rate
- Memory — used vs.
maxmemory, fragmentation ratio, evicted keys (Δ since last check) - Persistence — last RDB bgsave / AOF write status
- Clients & throughput — connected/blocked clients, rejected connections (Δ), ops/sec
- Keyspace — key count for the DB you care about
- Your own metrics — add custom gauges with a closure
Installation
composer require sunnysahijwani/redis-health-digest
Publish the config (thresholds, recipients, connection):
php artisan vendor:publish --tag=redis-health-digest-config
Requires PHP 8.2+ and Laravel 10, 11, 12, or 13. The service provider is auto-discovered.
Usage
# Send the digest email (the "real" run — updates delta baselines) php artisan redis:health-digest # Pretty table in your terminal (great for a quick look or a screenshot) php artisan redis:health-digest --console # Machine-readable JSON to stdout (pipe into other tooling) php artisan redis:health-digest --json # Collect + evaluate, skip the email php artisan redis:health-digest --no-mail # Override the connection / DB index ad-hoc php artisan redis:health-digest --console --connection=cache --db=5
Schedule it
// app/Console/Kernel.php (Laravel 10) · routes/console.php (Laravel 11) $schedule->command('redis:health-digest')->dailyAt('09:00');
Sample console output
OK Redis Health Digest — production
+---------------------+----------------------------------+
| Metric | Value |
+---------------------+----------------------------------+
| Sampled at | 2026-08-12T09:01:55+02:00 |
| Redis | 7.2.4 (master) |
| DB 5 keys | 104 |
| Used memory | 147.74 MB |
| Fragmentation | 1.12 |
| Hits / Misses | 1,043,712 / 408,168 |
| Hit rate | 71.89 % |
| Evicted (Δ) | 0 (0) |
| Rejected conns (Δ) | 0 (0) |
| Clients | 12 connected, 0 blocked |
| Ops/sec | 35 |
| Persistence | RDB ok / AOF ok |
+---------------------+----------------------------------+
• All thresholds within limits
Sample JSON
{
"status": "OK",
"environment": "production",
"sampled_at": "2026-08-12T09:01:55+02:00",
"memory": { "used_mb": 147.74, "fragmentation_ratio": 1.12, "evicted_keys_delta": 0 },
"cache": { "hits": 1043712, "misses": 408168, "hit_rate": 71.89 },
"persistence": { "rdb_last_bgsave_status": "ok", "aof_last_write_status": "ok" },
"reasons": ["All thresholds within limits"]
}
Configuration
Everything lives in config/redis-health-digest.php and every value has an env override. The status is the worst of every breached check.
'thresholds' => [ 'min_hit_rate' => 50.0, // WARN below this % 'max_used_memory_mb' => 512, // WARN above this 'max_fragmentation_ratio' => 1.5, // WARN above this 'max_evicted_keys_delta' => 0, // WARN if keys evicted since last check 'max_rejected_conn_delta' => 0, // WARN if connections rejected since last check 'require_persistence_ok' => true, // CRITICAL if RDB/AOF status != ok ],
| Env var | Default | Purpose |
|---|---|---|
REDIS_HEALTH_CONNECTION |
default |
Redis connection to sample |
REDIS_HEALTH_DB |
0 |
Logical DB index to report key count for |
REDIS_HEALTH_MAIL_TO |
— | Comma-separated recipients |
REDIS_HEALTH_MAIL_ONLY_ALERTS |
false |
Only email on WARN/CRITICAL (quiet when healthy) |
REDIS_HEALTH_MIN_HIT_RATE |
50 |
Minimum acceptable hit rate (%) |
REDIS_HEALTH_MAX_MEMORY_MB |
512 |
Memory ceiling (MB) |
Deltas (
evicted_keys,rejected_connections) are cumulative counters, so the digest stores a snapshot on each email run and reports the change since the previous run.--json/--consoleinspections never disturb that baseline.
Custom gauges
Add your own metric to every digest:
'custom_gauges' => [ 'Default queue length' => fn (array $info) => (int) Redis::llen('queues:default'), ],
The closure receives the parsed INFO map and returns a scalar.
Alert-only, non-zero exit
A CRITICAL status makes the command exit non-zero, so cron/CI can alert on it:
php artisan redis:health-digest --no-mail --console || notify-my-pager "Redis unhealthy"
Testing
composer install
composer test
The suite runs entirely against fixture INFO payloads — no live Redis, no database, nothing sent — via a fakeable RedisInfoProvider.
How it works
INFO (read-only) ──▶ InfoParser ──▶ MetricsCollector ──▶ DigestReport
│
ThresholdEvaluator ◀───────────┘
│
┌───────────────────┼───────────────────┐
Mailable JSON (stdout) Console table
Metric collection is decoupled from Redis behind a RedisInfoProvider contract, which keeps it client-agnostic (phpredis / predis) and trivially testable.
License
MIT © Sunny Sahijwani