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 locally

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.2.tar.gz (18.2 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.2-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

Details for the file ironops-1.0.2.tar.gz.

File metadata

  • Download URL: ironops-1.0.2.tar.gz
  • Upload date:
  • Size: 18.2 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.2.tar.gz
Algorithm Hash digest
SHA256 861c36ac9eaa51897c42372be60e77f524c283d5a455288b2995edbc173c8b02
MD5 7325b0d0693b8b89104bd6d9bebc724a
BLAKE2b-256 38531b8df4733cf7297ebff38031b146826410eda5302898ff219fea9c376b0e

See more details on using hashes here.

File details

Details for the file ironops-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: ironops-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 16.7 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b079fa635bd57a0a437575445e2fd7c526412ac8cd2742210828e46f0265ac63
MD5 146865b6ef643f71f297686e0c932ee0
BLAKE2b-256 54bca2643cd5a3e49b6754657cb9f02484dfb3eb22967b5b55ec9622534dfce4

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