DevOps CLI tool for remote server health monitoring over SSH
Built by Aziz Khemiri — A real solution for DevOps teams who want to stop worrying about their servers.
Imagine you have 10, 50, or 100 Linux servers scattered across your infrastructure. You need to know:
- Is the CPU spiking?
- Is the disk filling up?
- Are services running?
- Which servers are actually online right now?
IronOps answers all of these in seconds.
It connects to all your servers via SSH, runs health checks, and tells you exactly what's happening. No dashboards to set up. No complicated configuration. Just install it and go.
# Install once
pip install ironops
# Then use it anywhere
ironops --scan # Check all servers
ironops --status # Quick up/down summary
ironops --monitor # Watch them continuouslyDone. That's it.
| Problem | IronOps Solution |
|---|---|
| Too many servers to check manually | Scans them all in parallel (fast!) |
| Don't know which servers are down | Shows you instantly with --status |
| Need alerts when things break | Sends email when thresholds hit |
| Want to automate monitoring | Runs on cron schedule automatically |
| Setup is complicated | One command: ironops --init |
This is for everyone — install it globally and use it from anywhere.
# Install
pip install ironops
# Initialize your first project
mkdir my-monitoring
cd my-monitoring
ironops --init
# You're done! Edit config and run:
ironops --scanUse this if you want to modify the code or contribute.
git clone https://github.com/AzizKhemiri/ironops.git
cd ironops
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate # macOS/Linux
# .venv\Scripts\activate # Windows
# Install
pip install -e .
# Test it
ironops --help# Creates config/servers.yaml, .env, and directories
ironops --initOutput:
✓ Created config/
✓ Created logs/
✓ Created reports/
✓ Created .env
✓ Created config/servers.yaml
- IronOps initialized!
- Edit .env and config/servers.yaml
- Run: ironops --scan
Edit config/servers.yaml:
servers:
- name: "production-web"
host: "192.168.1.10" # Your server IP or hostname
port: 22
user: "ubuntu" # SSH user
auth: "key" # Use SSH key (I recommended)
key_path: "~/.ssh/id_rsa"
- name: "staging-db"
host: "staging.myapp.com"
port: 22
user: "deploy"
auth: "key"
key_path: "~/.ssh/deploy-key"
- name: "production-db"
host: "10.0.1.50"
port: 2222 # Custom SSH port
user: "admin"
auth: "key"
key_path: "~/.ssh/prod-key"
thresholds:
cpu_warning: 70 # Alert if CPU > 70%
cpu_critical: 90 # Alert if CPU > 90%
ram_warning: 75
ram_critical: 90
disk_warning: 80
disk_critical: 95
# Configure alerts
alerts:
email:
enabled: false
smtp_server: "smtp.gmail.com"
smtp_port: 587
sender_email: "${EMAIL_SENDER}" # From your .env
sender_password: "${EMAIL_PASSWORD}"
recipients:
- "team@myapp.com"
Make sure you can SSH to your servers:
# Generate SSH key (if you don't have one)
ssh-keygen -t rsa -b 4096 -C "ironops"
# Copy key to each server
ssh-copy-id -i ~/.ssh/id_rsa.pub ubuntu@192.168.1.10
ssh-copy-id -i ~/.ssh/id_rsa.pub deploy@staging.myapp.com
# Test it works
ssh ubuntu@192.168.1.10 "echo Success!"ironops --scanYou'll see something like:
██╗██████╗ ██████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗
██║██╔══██╗██╔═══██╗████╗ ██║██╔═══██╗██╔══██╗██╔════╝
██║██████╔╝██║ ██║██╔██╗ ██║██║ ██║██████╔╝███████║
██║██╔══██╗██║ ██║██║╚██╗██║██║ ██║██╔═══╝ ╚════██║
██║██║ ██║╚██████╔╝██║ ╚████║╚██████╔╝██║ ███████║
╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚══════╝
Scanning 3 server(s)...
✓ production-web (192.168.1.10) — OK
CPU: 34.2% ▓▓▓░░░░░░░ OK
RAM: 62.1% ▓▓▓▓▓▓░░░░ OK
Disk: 45.8% ▓▓▓▓░░░░░░ OK
Uptime: 45 days, 3 hours
✓ staging-db (staging.myapp.com) — OK
CPU: 8.1% ▓░░░░░░░░░ OK
RAM: 71.2% ▓▓▓▓▓▓▓░░░ OK
Disk: 82.5% ▓▓▓▓▓▓▓▓░░ ⚠ WARNING
Uptime: 12 days, 15 hours
✗ production-db (10.0.1.50) — UNREACHABLE
Error: Connection timed out
══════════════════════════════════════════════════
Summary: 3 servers | 2 OK | 1 WARNING | 1 UNREACHABLE
Report saved to: reports/health_20260308_143201.json
══════════════════════════════════════════════════
Run a one-time health check on all servers.
# Check all servers
ironops --scan
# Check only one server
ironops --scan --server production-web
# With custom config file
ironops --scan --config ~/my-servers.yamlQuick connectivity check — just tells you which servers are up or down.
ironops --statusOutput:
[UP] production-web 192.168.1.10
[UP] staging-db staging.myapp.com
[DOWN] production-db 10.0.1.50
══════════════════════════════════════════
2 UP | 1 DOWN | 3 total
══════════════════════════════════════════
Continuous monitoring — runs scans automatically every N seconds.
# Scan every 60 seconds (default)
ironops --monitor
# Scan every 30 seconds
ironops --monitor --interval 30
# Press Ctrl+C to stopInitialize configuration files in current directory.
Creates:
config/servers.yaml— your server list.env— your secrets (passwords, tokens)logs/andreports/directories
ironops --initAfter ironops --init, you get a .env file:
# IronOps Environment Variables
STAGING_SSH_PASSWORD=my_real_password_here
EMAIL_SENDER=monitor@myapp.com
EMAIL_PASSWORD=my_gmail_app_password
servers:
- name: "staging"
host: "staging.example.com"
user: "deploy"
auth: "password"
password: "${STAGING_SSH_PASSWORD}" # Read from .env# Check it's ignored
cat .gitignore | grep .env
# Should see: .envRun IronOps on a schedule so you don't have to manually check.
crontab -e
# Add this line:
*/15 * * * * cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&10 * * * * cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&10 8 * * * cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&10 9 * * 1 cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&1Cron Syntax Cheat Sheet:
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
│ │ │ │ │
│ │ │ │ │
* * * * *
*/15 * * * * = every 15 minutes
0 * * * * = every hour
0 8 * * * = every day at 8am
0 8 * * 1 = every Monday at 8am
0 0 1 * * = first day of every month
After each scan, IronOps saves a JSON report in reports/:
ls reports/
# health_20260308_143201.jsonFile Contents:
{
"run_id": 1,
"timestamp": "2026-03-08T14:32:01Z",
"summary": {
"total": 3,
"ok": 2,
"warning": 1,
"critical": 0,
"unreachable": 1
},
"servers": [
{
"name": "production-web",
"host": "192.168.1.10",
"status": "ok",
"metrics": {
"cpu_percent": 34.2,
"ram_percent": 62.1,
"disk_percent": 45.8,
"uptime": "45 days, 3 hours",
"os": "Linux 5.15.0-91-generic"
},
"alerts": []
},
{
"name": "staging-db",
"host": "staging.myapp.com",
"status": "warning",
"metrics": {
"cpu_percent": 8.1,
"ram_percent": 71.2,
"disk_percent": 82.5,
"uptime": "12 days, 15 hours"
},
"alerts": [
"WARNING — Disk: 82.5% (threshold: 80%)"
]
}
]
}Use this for:
- Trend analysis (track metrics over time)
- Integration with other tools (Elasticsearch, Grafana, etc.)
- Compliance reports
- Debugging
my-monitoring/
│
├── config/
│ └── servers.yaml # Your servers + settings
│
├── logs/
│ └── monitor.log # Scan logs
│
├── reports/
│ ├── health_20260308_143201.json
│ ├── health_20260308_143801.json
│ └── ... (one per scan)
│
├── .env # Your secrets
├── .env.example # Template
└── .gitignore
- Python 3.8+ — Language
- paramiko — SSH connections
- PyYAML — Config file parsing
- python-dotenv — Secret management
- argparse — CLI interface
- smtplib - Built-in Python email sender for alert
- Cron - Linux scheduler to run the script automatically (man cron)
- JSON - Report format
MIT — Aziz Khemiri.
Found a bug? Have a suggestion? Open an issue on GitHub: https://github.com/AzizKhemiri/ironops/issues