DevOps CLI tool for remote server health monitoring over SSH
Project description
IronOps
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.
What is IronOps?
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 continuously
Done. That's it.
Why IronOps?
| 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 |
Installation (2 Minutes)
Option 1: From PyPI (Recommended)
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 --scan
Option 2: From GitHub (For Development)
Use 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
Quick Start (5 Minutes)
Step 1: Initialize Your Project
# Creates config/servers.yaml, .env, and directories
ironops --init
Output:
✓ Created config/
✓ Created logs/
✓ Created reports/
✓ Created .env
✓ Created config/servers.yaml
- IronOps initialized!
- Edit .env and config/servers.yaml
- Run: ironops --scan
Step 2: Add Your Servers
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"
Step 3: Set Up SSH Access
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!"
Step 4: Run Your First Scan
ironops --scan
You'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
══════════════════════════════════════════════════
CLI Commands
ironops --scan
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.yaml
ironops --status
Quick connectivity check — just tells you which servers are up or down.
ironops --status
Output:
[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
══════════════════════════════════════════
ironops --monitor
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 stop
ironops --init
Initialize configuration files in current directory.
Creates:
config/servers.yaml— your server list.env— your secrets (passwords, tokens)logs/andreports/directories
ironops --init
Security & Secrets
Step 1: Create Your .env File
After 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
Step 2: Reference It in servers.yaml
servers:
- name: "staging"
host: "staging.example.com"
user: "deploy"
auth: "password"
password: "${STAGING_SSH_PASSWORD}" # Read from .env
Step 3: Make Sure .env is in .gitignore
# Check it's ignored
cat .gitignore | grep .env
# Should see: .env
Automate with Cron
Run IronOps on a schedule so you don't have to manually check.
Every 15 Minutes
crontab -e
# Add this line:
*/15 * * * * cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&1
Every Hour
0 * * * * cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&1
Every Day at 8 AM
0 8 * * * cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&1
Every Monday at 9 AM
0 9 * * 1 cd /path/to/my-monitoring && ironops --scan >> logs/cron.log 2>&1
Cron 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
Understanding the Report
JSON Report Example
After each scan, IronOps saves a JSON report in reports/:
ls reports/
# health_20260308_143201.json
File 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
🛠️ Project Structure to run ironops
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
Technologies Used
- 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
📄 License
MIT — Aziz Khemiri.
Contribution ?
Found a bug? Have a suggestion? Open an issue on GitHub: https://github.com/AzizKhemiri/ironops/issues
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ironops-1.0.1.tar.gz.
File metadata
- Download URL: ironops-1.0.1.tar.gz
- Upload date:
- Size: 13.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26eac25e2958e2cb180c9916a48cab8f3f575503ad1b8c2bb8c7d49b8cfa1dd1
|
|
| MD5 |
1f52f44a9d9b492cdbc6e3744a798150
|
|
| BLAKE2b-256 |
c5b7d49a2eb3fbe46c597327c544413c754c53500465497133dfa0d40467b6c5
|
File details
Details for the file ironops-1.0.1-py3-none-any.whl.
File metadata
- Download URL: ironops-1.0.1-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d0bec3bd331a3c8c67f40710d2fe591f166b030a9647b3d0fc8d03c92cce675
|
|
| MD5 |
14a11cd2760580952dedf8f22749206f
|
|
| BLAKE2b-256 |
67cd1d196b726fe8062796726e30dd79079ffeb1fad1f0790245c45e793a3b75
|