This folder contains an incident monitor that sends email and SMS alerts when the host is under stress.
π For detailed architecture and lifecycle information, see ARCHITECTURE.md
π¦ Current version: 1.0.0 (see CHANGELOG.md)
Note: This project uses a virtual environment (.venv) for Python dependencies. All commands should use the virtual environment's Python interpreter.
This project watches your Linux server so you can sleep like a deterministic state machine.
- If the server is healthy: silence.
- If the server is melting: email and optional SMS.
- If it recovers: one recovery notice, no dramatic monologues.
Run it fast on any server:
git clone https://github.com/iamsoorena/minimalerts.git && cd minimalerts && docker compose up --buildNo config present? It will ask for Gmail credentials on first run and generate config automatically.
Because sometimes you need alerts in minutes, not a monitoring platform in phases.
Prometheus + Alertmanager + Grafana is excellent, but usually means:
- multiple components to deploy and connect
- rules, dashboards, retention, and routing to configure
- more moving parts than "I just need alerts on this one server"
Other good tools:
- Netdata
- Zabbix
- Nagios
- Uptime Kuma
- Datadog / New Relic
All of them are valid. Most are not a clean "under 60 seconds to useful server alerts" path for a fresh box.
minimalerts is the boring fast path:
- one command
- interactive first-run config (or mounted config)
- email/SMS alerts
- done
Now that we have AI agents, we can build what we actually need in hours, so I did.
Think you can improve this more? Please don't hesitate to open a PR - AI will review and merge it.
- High CPU busy %
- High CPU
iowait - High CPU
steal - High swap-out rate
- High root disk usage %
- High root inode usage %
- Low
MemAvailable - High RAM usage %
- High load-per-CPU
- Monitored services not active (
ssh,docker) - Recovery notification when the host returns healthy
monitor.py: health check and notification senderconfig.sample.json: safe public templateconfig.json: channels, recipients, thresholds (local secret file)state.json: internal monitor state (auto-created)requirements.txt: Python dependency listDockerfile: container image definitiondocker-entrypoint.sh: container startup/config wizarddocker-compose.yml: one-command Docker deployment
You can run this project on any server with Docker in one command.
docker compose up --buildIf /data/config.json does not exist, the container will ask for:
- Optional server name label (for alert subjects/messages)
- Gmail address
- Gmail App Password
- Alert recipient emails
- Optional SMS credentials
After setup, it runs continuously and checks health every 5 minutes.
If your environment still starts non-interactive and cannot prompt, run:
docker compose run --rm minimalerts setup
docker compose up -ddocker run --name minimalerts -it \
--pid=host \
-v minimalerts-data:/data \
-v /:/hostfs:ro \
-e MONITOR_PROC_PATH=/proc \
-e MONITOR_ROOT_PATH=/hostfs \
-e MONITOR_INTERVAL_SEC=300 \
ghcr.io/iamsoorena/minimalerts:latestThe latest image is automatically published from main via GitHub Actions.
docker run -d --name minimalerts --restart unless-stopped \
--pid=host \
-v minimalerts-data:/data \
-v /:/hostfs:ro \
-e MONITOR_PROC_PATH=/proc \
-e MONITOR_ROOT_PATH=/hostfs \
-e SERVER_NAME="prod-api-1" \
-e SMTP_USER="your-email@gmail.com" \
-e SMTP_PASSWORD="your16charapppass" \
-e EMAIL_RECIPIENTS="ops1@example.com,ops2@example.com" \
-e SMS_ENABLED="false" \
ghcr.io/iamsoorena/minimalerts:latest# first create local data folder and put your config there
mkdir -p ./monitor-data
cp config.sample.json ./monitor-data/config.json
# optional: set "server_name" in ./monitor-data/config.json
docker run -d --name minimalerts --restart unless-stopped \
--pid=host \
-v $(pwd)/monitor-data:/data \
-v /:/hostfs:ro \
-e MONITOR_PROC_PATH=/proc \
-e MONITOR_ROOT_PATH=/hostfs \
ghcr.io/iamsoorena/minimalerts:latest# logs
docker logs -f minimalerts
# run one health check
docker exec -it minimalerts python3 /app/monitor.py --run-once
# verify Docker sees host-level metrics (CPU/RAM/disk)
docker exec -it minimalerts python3 /app/monitor.py --verify-host
# test notifications
docker exec -it minimalerts python3 /app/monitor.py --test-alertIf the container is already running:
# email-only test
docker compose exec minimalerts python3 /app/monitor.py --test-email
# email + SMS test (SMS only if enabled in config)
docker compose exec minimalerts python3 /app/monitor.py --test-alertIf you prefer one-off commands without entering the running container:
# email-only test
docker compose run --rm minimalerts test-email
# email + SMS test
docker compose run --rm minimalerts test-alertUse this command to verify the container is reading full host metrics (not just container scope):
docker compose exec minimalerts python3 /app/monitor.py --verify-hostLook for:
warnings: [](empty is good)monitor_paths.root_pathset to/hostfs- no warning about container filesystem or PID namespace
If you run with a named volume (example: -v minimalerts-data:/data), config.json lives at /data/config.json inside that volume.
# open shell in running container
docker exec -it minimalerts sh
# edit config
vi /data/config.json
# or
nano /data/config.json
# exit shell, then restart container to apply immediately
docker restart minimalertsIf you still prefer local editing, use copy-edit-copy:
docker cp minimalerts:/data/config.json ./config.json
# edit locally with your editor
docker cp ./config.json minimalerts:/data/config.json
docker restart minimalertsIf you use a bind mount (example: -v $(pwd)/monitor-data:/data), edit directly on host:
nano ./monitor-data/config.json
docker restart minimalertsgit pull
docker compose pull
docker compose up -d --builddocker pull ghcr.io/iamsoorena/minimalerts:latest
docker rm -f minimalerts
docker run -d --name minimalerts --restart unless-stopped \
--pid=host \
-v minimalerts-data:/data \
-v /:/hostfs:ro \
-e MONITOR_PROC_PATH=/proc \
-e MONITOR_ROOT_PATH=/hostfs \
ghcr.io/iamsoorena/minimalerts:latestThe installation script guides you through configuration and automatically sets up everything:
# Clone or download the repository
git clone https://github.com/iamsoorena/minimalerts.git
cd minimalerts
# Run the interactive installation (requires root/sudo)
sudo ./install.shWhat the installation script does:
- π§ Interactive Configuration: Guides you through server name, email (required), and SMS setup
- π§ Email Setup: Helps configure Gmail with step-by-step instructions
- π± SMS Setup: Optional IPPanel SMS configuration
- βοΈ Thresholds: Customize monitoring thresholds or use defaults
- β Validation: Tests configuration before proceeding
- π§ Mandatory Email Test: Sends email-only test and requires confirmation
- π Security: Sets proper file permissions
- π€ Systemd Setup: Installs automatic 5-minute monitoring only after email is confirmed
- β Verification: Ensures everything works before completion
Before running the installation, prepare your Gmail credentials:
- Enable 2-Factor Authentication on your Gmail account
- Generate App Password:
- Go to https://myaccount.google.com/security
- Click "2-Step Verification" β "App passwords"
- Select "Mail" and "Other (custom name)"
- Enter "Server Monitor" as the name
- Copy the 16-character password (ignore spaces)
- Installation will prompt for your Gmail address and app password
For SMS alerts via IPPanel:
- Sign up at https://ippanel.com
- Get your API Key from dashboard
- Create a SMS pattern for alerts
- Have your sender number ready
If you prefer manual setup:
# Copy configuration
cp config.sample.json config.json
# Edit config.json with your credentials
# Create virtual environment
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
# Install systemd files (replace default path with your current path)
sudo sed "s#/opt/server-alerts#$(pwd -P)#g" server-health-monitor.service | sudo tee /etc/systemd/system/server-health-monitor.service >/dev/null
sudo cp server-health-monitor.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now server-health-monitor.timerconfig.json, state.json, and .venv are ignored by git to avoid leaking secrets.
- Set
server_nameinconfig.jsonto control how the host appears in alert messages. - For Docker env-based setup, use
SERVER_NAME. - If
server_nameis empty or missing, alerts automatically include host identity fromipinfo.io(same idea ascurl ipinfo.io) plus hostname.
After installation, the monitor runs automatically every 5 minutes using systemd timers:
- Service:
server-health-monitor.service- Executes the monitoring check - Timer:
server-health-monitor.timer- Triggers the service every 5 minutes - On Boot: Starts 5 minutes after system boot
- Persistent: Catches up on missed runs after reboots
Timer: server-health-monitor.timer
βββ Runs every: 5 minutes
βββ Accuracy: Β±1 minute
βββ On boot delay: 5 minutes
βββ Persistent: Yes (catches up after downtime)
# Check if timer is active
systemctl status server-health-monitor.timer
# See next run time
systemctl list-timers server-health-monitor.timer
# View execution logs
journalctl -u server-health-monitor.service -fAfter modifying config.json (thresholds, recipients, API keys, etc.), the changes take effect immediately for manual runs. However, for automated monitoring:
# Test new configuration immediately
.venv/bin/python monitor.py --self-test
# Send test alert with new configuration
.venv/bin/python monitor.py --test-alert# Check service status
systemctl status server-health-monitor.service
systemctl status server-health-monitor.timer
# View recent logs
journalctl -u server-health-monitor.service -n 20 --no-pager
journalctl -u server-health-monitor.timer -n 10 --no-pager
# Restart timer (next run will use new config)
systemctl restart server-health-monitor.timer
# Force immediate check (bypasses timer)
systemctl start server-health-monitor.serviceIf you're using cron instead of systemd timers, no restart is needed - the next scheduled run will automatically use the updated configuration.
# Install automatically (run as root)
sudo ./install.sh
# Check service status
systemctl status server-health-monitor.timer
systemctl status server-health-monitor.service
# View next scheduled run
systemctl list-timers server-health-monitor.timer
# View service logs
journalctl -u server-health-monitor.service -f
journalctl -u server-health-monitor.timer -f# Run one health check
.venv/bin/python monitor.py --run-once
# Run internal self-test (no notifications sent)
.venv/bin/python monitor.py --self-test
# Send test alert (email + SMS)
.venv/bin/python monitor.py --test-alert
# Send test email only
.venv/bin/python monitor.py --test-email# Edit configuration
nano config.json
# Restart timer after config changes
sudo systemctl restart server-health-monitor.timer
# Force immediate check
sudo systemctl start server-health-monitor.service
# View recent logs
journalctl -u server-health-monitor.service -n 20 --no-pagercpu_busy_percent:90iowait_percent:40steal_percent:10swap_out_per_sec:30mem_used_percent:88load1_per_cpu:3.0disk_used_percent:90inode_used_percent:90consecutive_failures:3cooldown_minutes:20
These values are a balanced production baseline: sensitive enough to catch real incidents, but resistant to one-off spikes.