Skip to main content

DevOps CLI tool for remote server health monitoring over SSH

Project description

IronOps

DevOps CLI tool for remote server health monitoring over SSH

Python PyPI License Version

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/ and reports/ 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ironops-1.0.1.tar.gz (13.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ironops-1.0.1-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

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

Hashes for ironops-1.0.1.tar.gz
Algorithm Hash digest
SHA256 26eac25e2958e2cb180c9916a48cab8f3f575503ad1b8c2bb8c7d49b8cfa1dd1
MD5 1f52f44a9d9b492cdbc6e3744a798150
BLAKE2b-256 c5b7d49a2eb3fbe46c597327c544413c754c53500465497133dfa0d40467b6c5

See more details on using hashes here.

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

Hashes for ironops-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8d0bec3bd331a3c8c67f40710d2fe591f166b030a9647b3d0fc8d03c92cce675
MD5 14a11cd2760580952dedf8f22749206f
BLAKE2b-256 67cd1d196b726fe8062796726e30dd79079ffeb1fad1f0790245c45e793a3b75

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page