Case study · 2025 – 2026 · Runs on my own servers

Server monitoring that talks to me: a Telegram alerting bot for SSH logins, service health and tampered repositories

I run my own servers, so I wrote the monitoring I wanted: a bot that tells me — in Telegram, within minutes — when someone logs in from an address it has never seen, when a revoked key is used, when a service dies, and, since a real incident, when one of my GitHub repositories is tampered with.

PythonBashLinuxTelegram Bot APIsystemdcronSecurity monitoring
Role
Sole developer and operator
Generations
v1 Bash + cron (in production) · v2 Python, standard library only
Covers
Two Linux servers and 11 GitHub repositories
Cost
One private Telegram chat, zero third-party services

Why not a SaaS

Because the interesting signals are on the box: auth.log, authorized_keys, systemctl, df. An agent that ships all of that to a vendor was more surface than I wanted, and every hosted tool I tried buried the one alert that mattered under dashboards. The whole point was fewer messages, each one worth reading.

Generation 1 — Bash and cron, in production

Three scripts, three cron entries, and curl to the Telegram sendMessage endpoint. Send-only by design: nothing listens on the network.

What monitor.sh alerts on:

Design details that keep it quiet:

The repository watcher

This part exists because of an incident I wrote up separately: malware injected into my repositories by rewriting legitimate commits. After cleaning up, I wanted to know within half an hour if it happened again.

repo-guard.sh is read-only and works without any GitHub token for public repositories:

  1. It records the SHA of every branch of every watched repository with git ls-remote. A SHA that moves backwards, a branch that disappears or a force-push shows up as a diff against the last run.
  2. Changed repositories are shallow-cloned and scanned by content, never by file name: the obfuscated payload's signature strings, .woff2 "fonts" whose first bytes are not wOF2, VS Code tasks.json files with a folderOpen trigger, abnormally large or single-line build config files, and commits whose committer differs from their author.
  3. Once a day it does a full scan instead of a shallow one.

Generation 2 — the Python rewrite

The Bash version is tied to my boxes. vps-sentinel is the portable one: a single Python file, standard library only, configured entirely through environment variables, shipped with a hardened systemd unit (dedicated user, NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, one writable state directory).

It adds what the cron scripts cannot do:

What I learned running it

Most alerting systems fail by being ignored. Most of the rules above exist because of an alert that was once noise: the flapping service, the deploy key that logged in on every deploy, the daily "all good" that stopped being read. The current version sends me a handful of messages a week, which is the only reason I still read them.

Keep reading