Most people start thinking about monitoring after the first incident: the disk fills up with logs, the database dies from lack of memory, and you learn about it from your users. Basic protection can be set up in an evening, and faster if you don't assemble the stack by hand.
Here is the minimal setup that is enough for one VPS. Prometheus collects metrics, Grafana shows them, and Alertmanager sends messages to Telegram. Everything runs with Docker Compose. I packaged the ready-made configs into a free kit, Monitoring Lite, and the commands below come from it.
What you get
- four containers: Prometheus, node-exporter, Alertmanager, Grafana;
- one Host Overview dashboard: CPU, memory, disk usage by mountpoint, network, load average, uptime;
- five alerts in Telegram: host down, high CPU, high memory, disk almost full, host rebooted;
- a Grafana password generated at install time, and ports that are closed to the outside world.
How it fits together
node-exporter reads the host's /proc and /sys and serves metrics over HTTP. Prometheus scrapes them every 15 seconds and stores them. Alert rules are evaluated inside Prometheus: when an expression stays true for the configured time, the alert goes to Alertmanager, which groups messages and sends them to Telegram. Grafana reads data from Prometheus.
The datasource and dashboard are provisioned automatically, so you don't create anything by hand in the UI.
Step 1: a Telegram bot
- Message @botfather in Telegram, send
/newbot, and pick a name. You get a token like123456789:AAExxxxxxxx. - Send any message to your new bot. Until you do, it cannot write to you first.
- Open
https://api.telegram.org/bot<TOKEN>/getUpdatesin a browser and find"chat":{"id": ...in the response. That's yourchat_id. For a group, add the bot to the group and post something there; the ID will be a negative number.
Step 2: install
You need Docker and Docker Compose. Download the kit: https://devkit-shop.surge.sh/free/devops-monitoring-lite-v1.0.zip, unpack it, and run the installer:
unzip devops-monitoring-lite-v1.0.zip
cd devops-monitoring-lite
TG_BOT_TOKEN=123456789:AAExxxxxxxx TG_CHAT_ID=987654321 ./install.sh
TG_BOT_TOKEN and TG_CHAT_ID are only needed for this run: the installer saves them to .env as TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID. If you don't pass them and they aren't in .env yet, the script asks for the token and chat_id interactively. It:
- checks for Docker and
docker compose(or the olderdocker-compose); - creates
.envand generates a Grafana admin password in it; - validates the token and chat_id format so a malformed value can't break the YAML;
- renders the Alertmanager config from a template with your token and chat_id;
- runs
docker compose up -dand restarts Alertmanager so it picks up the fresh config.
You can safely re-run it: the Grafana password and other values in .env are kept. To change the token, run the installer again with a new TG_BOT_TOKEN, or edit .env and run ./install.sh.
The token is injected at render time and is not stored in the compose file. The generated alertmanager.yml and .env are both listed in the kit's .gitignore, so they stay out of git.
Step 3: reach Grafana
All ports are bound to 127.0.0.1 on purpose. Prometheus and Alertmanager have no authentication, so they should not be exposed to the internet. From your computer, use an SSH tunnel:
ssh -L 3000:127.0.0.1:3000 user@your-server
Open http://localhost:3000. The login is admin; the password is in .env:
grep GRAFANA_ADMIN_PASSWORD .env
Find the "Host Overview — Сервер" dashboard in the "Monitoring Lite" folder. It shows CPU, memory, disk and network graphs, load average and uptime (the archive includes a sample screenshot, host-overview.png).
You can open access by IP by setting BIND_ADDR=0.0.0.0 in .env and running docker compose up -d. But this setting is shared, so all four ports go public: 3000, 9090, 9093 and 9100. There's a second trap: ports published by Docker bypass ufw rules, so ufw deny 9090 won't close them. You'd have to block them with your hosting provider's firewall or with rules in the iptables DOCKER-USER chain. If in doubt, keep 127.0.0.1 and use the tunnel.
Step 4: verify the alert path
First, check that Prometheus sees the targets and has loaded the rules:
curl -s http://127.0.0.1:9090/api/v1/targets | grep -o '"health":"[a-z]*"'
curl -s http://127.0.0.1:9090/api/v1/rules | grep -o '"name":"[A-Za-z]*"'
Both targets (Prometheus itself and node-exporter) should report "health":"up", and the second command should list five rule names. Now send a test alert:
./scripts/send-test-alert.sh
The script POSTs to the Alertmanager API. Within 30–60 seconds, a Telegram message should arrive with the header "🔴 ВНИМАНИЕ" (Russian for "ALERT"), followed by the line "TestAlert (warning)" and the text "Это тестовый алерт" ("This is a test alert"). The delay comes from group_wait: 30s, which lets Alertmanager batch related alerts into one message.
If nothing arrives, check the logs:
docker compose logs alertmanager | grep -i telegram
Most often the cause is that you never messaged the bot, or the token has a typo.
The rules
The rules live in config/prometheus/alert_rules.yml.
| Alert | Condition | for |
|---|---|---|
InstanceDown |
up{job="node-exporter"} == 0 |
2 min |
HighCPU |
average CPU load > 90% | 10 min |
HighMemory |
more than 90% of RAM used, based on MemAvailable
|
5 min |
DiskSpace |
disk more than 85% full | 5 min |
HostRebooted |
time() - node_boot_time_seconds < 600 |
none |
The for clause prevents false alarms: a short CPU spike during a build or a backup won't wake you up at night. HostRebooted has no for, so it fires right after boot and stays active for the first 10 minutes.
The DiskSpace rule ignores tmpfs, overlay, squashfs, devtmpfs, all fuse.* types (such as fuse.sshfs), nfs and nfs4, cifs, smb3 and autofs. Without this filter, it fires on system and network mounts you don't manage. For memory, the rule uses MemAvailable rather than MemFree: Linux uses spare memory for cache, so "free" memory is almost never large.
Thresholds are edited in the same file. After a change, reload the rules without restarting:
curl -X POST http://127.0.0.1:9090/-/reload
This works because Prometheus runs with --web.enable-lifecycle.
Adding a second server
Run node-exporter on the second server and add it to config/prometheus/prometheus.yml under the node-exporter job as a separate entry with its own label:
- job_name: node-exporter
static_configs:
- targets: ["node-exporter:9100"]
labels:
instance_name: "main-server"
- targets: ["10.0.0.5:9100"]
labels:
instance_name: "server-2"
Keep the instance_name label on the first server as is; it's already in the kit's config. Then run docker compose restart prometheus. Don't expose port 9100 to the internet; send the traffic over a VPN or private network.
Common problems
The Grafana container won't start. Check docker compose logs grafana. The usual cause is permissions on a volume left over from a previous install. If you don't need the data, ./uninstall.sh --purge-data followed by ./install.sh helps.
A Prometheus target is DOWN. Open the targets page through the tunnel at http://127.0.0.1:9090/targets; it shows the reason, such as connection refused or context deadline exceeded. Usually the service hasn't started yet, or there's a typo in the address in prometheus.yml.
Port already in use. If Grafana or Prometheus is already running on the server, change the relevant variables in .env (GRAFANA_PORT, PROMETHEUS_PORT, ALERTMANAGER_PORT, NODE_EXPORTER_PORT) and run docker compose up -d. Remember to change the port in the curl commands and the SSH tunnel from this article too.
Telegram wasn't configured during install. Alertmanager still starts, just without sending anything. Fill in TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID in .env and run ./install.sh again.
Stopping and removing
./uninstall.sh # stop, keep data
./uninstall.sh --purge-data # stop and delete data
What's not in this kit
The kit is deliberately limited to one server. It doesn't watch Docker containers, check website availability or SSL certificate expiry, collect logs, or handle backups. If you need those, you can add them yourself: cAdvisor for containers, blackbox-exporter for sites, Loki for logs.
The full version
The paid DevOps Monitoring Starter Kit covers those gaps: cAdvisor, blackbox-exporter for site and SSL checks, three dashboards (host, Docker containers, sites and SSL), 11 alert rules, a backup script, and optionally Loki with Promtail for logs and HTTPS via Caddy. Details are at https://devkit-shop.surge.sh/, where you can pay with USDT (TRC20) or, in Russia, by card.
Disclosure: this article and the kit were prepared by an AI assistant working on behalf of the kit's author, and the commands were checked against the kit files.












