[{"data":1,"prerenderedAt":215},["ShallowReactive",2],{"seo-verification":3,"blog-disk-full-and-ram-alerts-on-a-vps-no-monitoring-stack-needed-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-disk-full-and-ram-alerts-on-a-vps-no-monitoring-stack-needed-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":30,"intro":33,"sections":34,"ctaTitle":155,"ctaBody":156,"ctaButton":157,"ctaUrl":158,"relatedPosts":159},506,"disk-full-and-ram-alerts-on-a-vps-no-monitoring-stack-needed",{"fr":12,"en":10,"ar":13,"es":14},"alertes-disque-ram-vps-monitoring-proactif","تنبيهات-امتلاء-القرص-والذاكرة-على-vps-دون-منظومة-مراقبة","alertas-de-disco-lleno-y-ram-en-un-vps-sin-pila-de-supervision","Disk full and RAM alerts on a VPS, no monitoring stack needed","Six shell alerts for your client VPS: disk, inodes, OOM, systemd services, certificates, backups, plus a heartbeat that watches the check itself.",13,0,false,"2026-10-27T00:00:00+00:00","2026-10-27T23:58:39+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},8,"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Falertes-disque-ram-vps-monitoring-proactif-poster.svg","A disk fills up, the database stops, and the client finds out before you do. The cost is an emergency call-out and shaken trust. A full monitoring stack is a second server to maintain, so many agencies give up and monitor nothing. This guide sets up six minimum shell alerts on every VPS: disk, inodes, memory, services, certificates, backups. The check is itself watched by an external heartbeat.",[35,39,50,53,56,87,90,93,96,132,136,139,149,152],{"type":36,"title":37,"body":38},"h2","A full disk warns nobody","The scenario keeps coming back. A client VPS has run for months, the database writes its logs, backups pile up, and one morning the service stops. No alert went out, and the client finds out before you do. On PostgreSQL, a case reported on the project's official mailing list shows the exact line: `PANIC: could not write to file \"pg_wal\u002Fxlogtemp.11399\": No space left on device`. A PANIC is not a mere warning: the server can no longer log its writes, it stops, and it only restarts cleanly once space has been freed.\n\nThe hard part is not detecting a full disk, it is detecting it beforehand. Between 80% usage and the outage there are often hours or days, and that is exactly the window a well-tuned alert must catch. This guide does not redo the installation of a monitoring stack: it puts the useful minimum on every VPS, in shell, with no second server to maintain.",{"type":40,"title":41,"items":42},"ul","The six minimum alerts and the heartbeat, on every VPS",[43,44,45,46,47,48,49],"**Disk** — two thresholds, 80% as a warning and 90% as critical, on every mount point that matters.","**Inodes** — a disk can refuse writes while bytes remain free, if inodes are exhausted.","**Memory and OOM** — detect that the kernel killed a process, not only that RAM is high.","**systemd services** — get notified when the database, web server or proxy enters the failed state.","**Certificates** — alert 21 days before expiry, when automatic renewal has failed silently.","**Backups** — alert on the absence of a recent success, not only on an explicit error.","**The check itself** — an external heartbeat that warns you when alerts stop being sent.",{"type":36,"title":51,"body":52},"Prerequisites in numbers: almost nothing","Everything below fits in a few dozen lines of POSIX shell. You need a Linux VPS with `curl`, `cron` or systemd timers, root access to write in `\u002Fusr\u002Flocal\u002Fbin` and `\u002Fetc`, and about 1 MB of disk. The memory cost is that of a script lasting one second every five minutes: negligible, even on the smallest VPS.\n\nTwo external pieces complete the setup: a receiving channel (a Telegram bot, or any webhook reachable over HTTP) and a heartbeat service, which receives a regular ping and warns you when it stops. For that second point, Healthchecks.io offers a free plan limited to 20 jobs and 100 log entries per job, which covers a small fleet comfortably; check those limits at the source before relying on them. If you want no third-party service, Uptime Kuma's push mode plays the same role on a machine of your own.",{"type":36,"title":54,"body":55},"Disk and inodes: the thresholds and what shifts them","The useful command is `df -h --output=pcent,target`: it prints only the percentage and the mount point, which is easy to read and parse. For inodes, the same idea becomes `df --output=ipcent,target`. A sessions folder or a pile of small cache files can use up every inode of an ext4 file system while `df -h` still shows 40% usage: the write fails all the same with `No space left on device`.\n\nThere is a second subtlety. On ext4, 5% of blocks are reserved by default for privileged processes, according to the `tune2fs` manual page; that is what lets system daemons keep writing when an ordinary user has filled the disk. Consequence: a service running under an unprivileged account, such as the database, sees `df` report 100% while the disk is about 95% used. Hence the thresholds chosen: 80% to have time to act, 90% to act immediately. Waiting for 100% means alerting after the outage.",{"type":57,"title":58,"steps":59},"steps","Set it all up in one hour",[60,63,66,69,72,75,78,81,84],{"title":61,"body":62},"Create the alert channel","Create a Telegram bot with BotFather, note its token and the identifier of the conversation that should receive messages. Every Bot API request follows the form `https:\u002F\u002Fapi.telegram.org\u002Fbot\u003Ctoken>\u002FMETHOD_NAME`, and the sending method is `sendMessage`. If you prefer a webhook (Slack, Mattermost, ntfy), only the last line of the sending script changes.",{"title":64,"body":65},"Write the environment file","`sudo mkdir -p \u002Fvar\u002Flib\u002Falerte-vps`\n`sudo install -m 600 \u002Fdev\u002Fnull \u002Fetc\u002Falerte-vps.env`\nThen add four lines to it: `TG_TOKEN=...`, `TG_CHAT=...`, `HC_URL=https:\u002F\u002Fhc-ping.com\u002F\u003Cuuid>` (five-minute check) and `HC_URL_JOUR=https:\u002F\u002Fhc-ping.com\u002F\u003Cother-uuid>` (daily check). Mode 600 keeps the token readable by root only.",{"title":67,"body":68},"Write the sending script with repeat suppression","Save these lines in `\u002Fusr\u002Flocal\u002Fbin\u002Falerte-envoi` then make the file executable with `sudo chmod 755`. The per-message lock file avoids being woken every five minutes for the same alert: an identical message is only sent again after one hour.\n`#!\u002Fbin\u002Fsh`\n`. \u002Fetc\u002Falerte-vps.env`\n`f=\u002Fvar\u002Flib\u002Falerte-vps\u002F$(echo \"$1\" | md5sum | cut -c1-12)`\n`[ -n \"$(find \"$f\" -mmin -60 2>\u002Fdev\u002Fnull)\" ] && exit 0`\n`touch \"$f\"`\n`curl -fsS -m 10 --retry 3 \"https:\u002F\u002Fapi.telegram.org\u002Fbot${TG_TOKEN}\u002FsendMessage\" -d chat_id=\"${TG_CHAT}\" --data-urlencode text=\"$(hostname) $1\"`",{"title":70,"body":71},"Check disk and inodes","In `\u002Fusr\u002Flocal\u002Fbin\u002Falerte-rapide` (shebang `#!\u002Fbin\u002Fsh`), add the two loops below. The `grep -v` filter skips file systems that expose no inodes and print a dash.\n`df --output=pcent,target -x tmpfs -x devtmpfs | tail -n +2 | while read p m; do n=${p%\\%}; if [ \"$n\" -ge 90 ]; then alerte-envoi \"CRIT disk $m\"; elif [ \"$n\" -ge 80 ]; then alerte-envoi \"WARN disk $m\"; fi; done`\n`df --output=ipcent,target -x tmpfs -x devtmpfs | tail -n +2 | grep -v '^ *-' | while read p m; do [ \"${p%\\%}\" -ge 90 ] && alerte-envoi \"INODES $m\"; done`",{"title":73,"body":74},"Detect processes killed by the OOM killer","The `oom_kill` counter in `\u002Fproc\u002Fvmstat` only grows since boot. It is compared with the previous value, kept in a file; after a reboot the counter restarts from zero, so the comparison is wrong once, then recovers.\n`n=$(awk '\u002F^oom_kill\u002F {print $2}' \u002Fproc\u002Fvmstat)`\n`old=$(cat \u002Fvar\u002Flib\u002Falerte-vps\u002Foom 2>\u002Fdev\u002Fnull || echo 0)`\n`[ \"$n\" -gt \"$old\" ] && alerte-envoi \"OOM kill\"`\n`echo \"$n\" > \u002Fvar\u002Flib\u002Falerte-vps\u002Foom`",{"title":76,"body":77},"Add the heartbeat and schedule it","End `alerte-rapide` with the ping. It is only reached if the script ran to the end; if cron stops, if the disk goes read-only or if the script crashes, the ping stops and the external service warns you. Then add the cron lines in `\u002Fetc\u002Fcron.d\u002Falerte-vps`.\n`. \u002Fetc\u002Falerte-vps.env; curl -fsS -m 10 --retry 5 -o \u002Fdev\u002Fnull \"$HC_URL\"`\n`PATH=\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fbin:\u002Fbin`\n`*\u002F5 * * * * root \u002Fusr\u002Flocal\u002Fbin\u002Falerte-rapide`\n`30 7 * * * root \u002Fusr\u002Flocal\u002Fbin\u002Falerte-quotidien`",{"title":79,"body":80},"Check certificates and backups once a day","In `\u002Fusr\u002Flocal\u002Fbin\u002Falerte-quotidien`, add these lines. The `-checkend` option of `openssl x509` fails if the certificate expires within the given number of seconds; 1814400 seconds equal 21 days. The backup test alerts when no non-empty `.gz` file is newer than 1560 minutes, that is 26 hours. The script ends with its own ping, to the second check: without it, a daily check that fell silent would go unnoticed.\n`echo | openssl s_client -servername your-domain.com -connect your-domain.com:443 2>\u002Fdev\u002Fnull | openssl x509 -noout -checkend 1814400 || alerte-envoi \"CERT your-domain.com\"`\n`find \u002Fvar\u002Fbackups\u002Fapp -name '*.gz' -mmin -1560 -size +1k | grep -q . || alerte-envoi \"BACKUP absent\"`\n`. \u002Fetc\u002Falerte-vps.env; curl -fsS -m 10 --retry 5 -o \u002Fdev\u002Fnull \"$HC_URL_JOUR\"`",{"title":82,"body":83},"Hook the critical services to OnFailure","Create `\u002Fetc\u002Fsystemd\u002Fsystem\u002Fnotify-failure@.service` with the content below, then `sudo systemctl daemon-reload`.\n`[Unit]`\n`Description=Alerte echec de %i`\n`[Service]`\n`Type=oneshot`\n`ExecStart=\u002Fusr\u002Flocal\u002Fbin\u002Falerte-envoi \"FAILED %i\"`\nFor each service to watch, open a drop-in with `sudo systemctl edit mariadb.service` and add these two lines.\n`[Unit]`\n`OnFailure=notify-failure@%n.service`",{"title":85,"body":86},"Force an alert to test, then clean up","An alert that never fired is not an alert. First check the free space, create a file big enough to cross the threshold, run the script by hand, then delete the file. If the message does not arrive, the defect is in the channel, and it is better to find out now.\n`df -h --output=pcent,target \u002Fvar\u002Ftmp`\n`sudo fallocate -l 2G \u002Fvar\u002Ftmp\u002Ftest-alerte.img`\n`sudo \u002Fusr\u002Flocal\u002Fbin\u002Falerte-rapide`\n`sudo rm \u002Fvar\u002Ftmp\u002Ftest-alerte.img`",{"type":36,"title":88,"body":89},"Memory: kernel OOM or cgroup limit","Two mechanisms kill a process for lack of memory, and they are not read in the same place. The first is the kernel OOM killer, when the whole machine runs short of RAM: the kernel log then holds a message like `Out of memory: Killed process`, which `journalctl -k` lets you find. The second is a cgroup limit. According to the systemd documentation, when a unit's usage cannot be contained under `MemoryMax=`, the OOM killer is invoked inside the unit: the rest of the machine may have free memory.\n\nThe `oom_kill` counter in `\u002Fproc\u002Fvmstat` covers the first case at machine scale. For a specific unit, the `memory.events` file of its cgroup exposes its own `oom_kill` counter, defined by the kernel documentation as the number of processes of the cgroup killed by any kind of OOM killer. On a VPS that hosts a hungry application, such as the one described in \u003Ca href=\"\u002Fblog\u002Fn8n-erreurs-memoire-oom-production-vps\">the guide on n8n and OOM in production\u003C\u002Fa>, this second counter explains the stops that `free -h` never shows.",{"type":36,"title":91,"body":92},"OnFailure: what it covers and what it does not","According to the systemd documentation, `OnFailure=` lists the units activated when the unit enters the failed state. The `notify-failure@.service` template receives the faulty unit's name through the `%n` specifier, and the alert message can attach the output of `systemctl status` or `journalctl -u` for context.\n\nThe limit is structural: a service that runs but no longer answers never went through the failed state, so `OnFailure=` says nothing. A service restarted by `Restart=` only gets there once the start limit is reached. For those cases, add a simple application probe, for example a `curl` on the application's health address, in the fast script. The syntax of timers and cron is covered in \u003Ca href=\"\u002Fblog\u002Fcron-systemd-timers-automatisation-vps\">the cron and systemd timers guide\u003C\u002Fa>, and there is no point repeating it here.",{"type":36,"title":94,"body":95},"Certificates and backups: alert on the absence of success","An automatic certificate renewal can fail silently for weeks: a modified DNS record, a blocked port, a broken plugin. The certificate does not expire that day, it expires ninety days later, and nobody remembers. The `openssl` test queries what the visitor actually receives, which beats reading a file on disk. On a server managed by Certbot, `certbot certificates` lists the certificates and their expiry, and `certbot renew --dry-run` checks that the next renewal will succeed.\n\nSame logic for backups. An alert triggered by the failure of a backup script does not see the case where the script never ran. The correct alert sits on the absence: no recent file, or a zero-size file. That is what the `find -mmin` line of the daily script does, and why it stays quiet when all is well and talks as soon as the backup chain stops, whatever the reason.",{"type":97,"title":98,"headers":99,"rows":103},"comparison","Home-made scripts or a monitoring stack",[100,101,102],"Criterion","Shell scripts and OnFailure","Beszel or Uptime Kuma",[104,108,112,116,120,124,128],[105,106,107],"Setup","One hour, a few files in `\u002Fusr\u002Flocal\u002Fbin`","A hub to host, agents to install on every VPS",[109,110,111],"Target fleet","1 to 5 VPS, up to 10 if thresholds are identical","Beyond 5 to 10 VPS, or as soon as comparison is needed",[113,114,115],"History and charts","None, only the moment of the alert","Charts, trends, comparison between machines",[117,118,119],"Maintenance","Nothing to update outside the shell","A second server and its updates to maintain",[121,122,123],"Application services","A `curl` probe to write yourself","HTTP and TCP probes and status page ready to use",[125,126,127],"Monitor failure","Detected by the external heartbeat","To watch separately, the hub is itself a machine",[129,130,131],"When to choose it","Minimum alerts, small fleet, no extra server","History wanted, growing fleet, shared team",{"type":133,"title":134,"body":135},"tip","Watch the watcher","The trap of these scripts is to become a false assurance: they stay silent because all is well, or because they are broken, and silence looks the same in both cases. The heartbeat ping solves it. Set the check's expected period slightly above the cron interval and add a grace margin: a silence alert arriving twenty minutes late beats a false positive at every reboot. Also keep `\u002Fetc\u002Falerte-vps.env` in mode 600 and never commit the token.",{"type":36,"title":137,"body":138},"Troubleshooting: the errors that keep coming back","The alerts themselves fail in predictable ways. Here are the cases met most often, with the message or symptom to recognise and the remedy. Each one is checked by hand before looking further.",{"type":40,"title":140,"items":141},"Messages and symptoms with their remedy",[142,143,144,145,146,147,148],"**`PANIC: could not write to file \"pg_wal\u002F...\": No space left on device`** — the volume holding PostgreSQL's logs is full. First free space outside PostgreSQL (rotated logs, old backups), never delete `pg_wal` files by hand, then restart the service.","**`No space left on device` while `df -h` shows free space** — inodes are exhausted. Run `df --output=ipcent,target`, find the folder holding millions of small files (sessions, cache) and purge it.","**Disk full but `du` cannot find the space** — deleted files are still held open by a process. `sudo lsof +L1` lists them; restarting the process frees the space.","**`oom_kill` rises, nothing in `journalctl -k`** — the kernel log rolled over, or the kill comes from a `MemoryMax=` limit. Read `memory.events` in the unit's cgroup directory and `journalctl -u` for the unit.","**A unit runs but no longer answers, no alert** — that is the limit of `OnFailure=`. Add a `curl` probe on the health address and alert on the return code.","**No Telegram message, no visible error** — cron mails the script output locally, where nobody reads it. Run `alerte-envoi test` by hand with `sudo` and read the `curl` response.","**The certificate nears expiry although Certbot is installed** — run `certbot renew --dry-run` and read the validation error, then fix the DNS or port involved.",{"type":36,"title":150,"body":151},"The official documentation","Every behaviour described here comes from a primary source. For `OnFailure=`, \u003Ca href=\"https:\u002F\u002Fwww.freedesktop.org\u002Fsoftware\u002Fsystemd\u002Fman\u002Flatest\u002Fsystemd.unit.html\">the systemd.unit manual page\u003C\u002Fa>; for `MemoryMax=`, \u003Ca href=\"https:\u002F\u002Fwww.freedesktop.org\u002Fsoftware\u002Fsystemd\u002Fman\u002Flatest\u002Fsystemd.resource-control.html\">systemd.resource-control\u003C\u002Fa>; for the `oom` and `oom_kill` counters, \u003Ca href=\"https:\u002F\u002Fdocs.kernel.org\u002Fadmin-guide\u002Fcgroup-v2.html\">the kernel's cgroup v2 documentation\u003C\u002Fa>; for the reserved 5%, \u003Ca href=\"https:\u002F\u002Fman7.org\u002Flinux\u002Fman-pages\u002Fman8\u002Ftune2fs.8.html\">the tune2fs manual page\u003C\u002Fa>. On the sending side, \u003Ca href=\"https:\u002F\u002Fcore.telegram.org\u002Fbots\u002Fapi#making-requests\">the Telegram Bot API\u003C\u002Fa> describes the request form, and \u003Ca href=\"https:\u002F\u002Fhealthchecks.io\u002Fdocs\u002Fhttp_api\u002F\">the Healthchecks API documentation\u003C\u002Fa> the ping addresses, with \u003Ca href=\"https:\u002F\u002Fhealthchecks.io\u002Fpricing\u002F\">the pricing page\u003C\u002Fa> for the free plan limits. For renewal, \u003Ca href=\"https:\u002F\u002Feff-certbot.readthedocs.io\u002Fen\u002Fstable\u002Fusing.html\">the Certbot documentation\u003C\u002Fa>. The PostgreSQL message quoted comes from \u003Ca href=\"https:\u002F\u002Fwww.postgresql.org\u002Fmessage-id\u002FBL0PR04MB6499BC32502F223A7BDF9BF2D0B30%40BL0PR04MB6499.namprd04.prod.outlook.com\">a thread on the project's official mailing list\u003C\u002Fa>.",{"type":36,"title":153,"body":154},"Simple alerts, the same on every VPS","Six alerts and a heartbeat do not replace a monitoring stack, they cover what really brings a server down: disk, inodes, memory, services, certificates, backups. The main gain is uniformity: same thresholds, same messages, same test gesture on every machine of the fleet. When the fleet grows or history becomes necessary, the move to a stack is prepared in \u003Ca href=\"\u002Fblog\u002Fself-host-beszel-vps\">the Beszel guide\u003C\u002Fa> and in \u003Ca href=\"\u002Fblog\u002Fmonitoring-alerte-proactif-self-hosted-vps\">the proactive alerts guide with Beszel and Uptime Kuma\u003C\u002Fa>, which cover the installation end to end.","A fleet of client VPS, uniform thresholds","On ServOrbit.com, the domains, hosting and servers of a client fleet are grouped in one place, in the same management space. The VPS administration option exists for clients who do not want to administer their own server.","See agency solutions","\u002Fsolutions\u002Fagences",[160,176,197],{"id":161,"slug":162,"slugs":163,"title":167,"excerpt":168,"readTime":169,"views":18,"isPinned":19,"publishedAt":170,"updatedAt":171,"category":172,"categories":173,"featuredImage":30,"bgImage":31,"posterImage":175,"relatedSolution":30},440,"proactive-vps-monitoring-beszel-uptime-kuma-v2",{"fr":164,"en":162,"ar":165,"es":166},"monitoring-alerte-proactif-self-hosted-vps","مراقبة-استباقية-vps-beszel-uptime-kuma","monitorizacion-proactiva-vps-beszel-uptime-kuma-v2","Proactive VPS monitoring: Beszel and Uptime Kuma v2","Deploy Beszel and Uptime Kuma v2 on a VPS to monitor your entire server fleet and receive Telegram or SMTP alerts before your clients notice the outage.",11,"2026-10-14T00:00:00+00:00","2026-10-14T23:33:30+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[174],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fmonitoring-alerte-proactif-self-hosted-vps-poster.svg",{"id":177,"slug":178,"slugs":179,"title":183,"excerpt":184,"readTime":185,"views":186,"isPinned":19,"publishedAt":187,"updatedAt":188,"category":189,"categories":194,"featuredImage":30,"bgImage":31,"posterImage":196,"relatedSolution":30},230,"cron-vs-systemd-timers-automate-your-linux-vps",{"fr":180,"en":178,"ar":181,"es":182},"cron-systemd-timers-automatisation-vps","cron-مقابل-systemd-timers-أتمتة-vps-linux","cron-vs-systemd-timers-automatizar-vps-linux","cron vs systemd timers: Automate Your Linux VPS","Practical comparison of cron and systemd timers: syntax, logs, migration and troubleshooting for recurring tasks on a Linux VPS.",12,1,"2026-08-06T00:00:00+00:00","2026-09-20T21:13:53+00:00",{"id":190,"name":191,"slug":192,"color":193,"icon":192},2,"Automation","automatisation","bg-brand-action\u002F10 text-brand-action",[195],{"id":190,"name":191,"slug":192,"color":193,"icon":192},"\u002Fblog\u002Fcovers\u002Fcron-systemd-timers-automatisation-vps-poster.svg",{"id":198,"slug":199,"slugs":200,"title":204,"excerpt":205,"readTime":23,"views":186,"isPinned":19,"publishedAt":206,"updatedAt":207,"category":208,"categories":209,"featuredImage":30,"bgImage":31,"posterImage":211,"relatedSolution":212},133,"self-host-beszel-multi-server-monitoring-hub-on-your-vps",{"fr":201,"en":199,"ar":202,"es":203},"self-host-beszel-vps","استضف-beszel-على-vps-مركز-مراقبة-متعدد-الخوادم","alojar-beszel-hub-monitorizacion-vps","Beszel on VPS: multi-server monitoring, alerts and security","Deploy Beszel on a VPS, monitor your entire fleet from one hub, set up Telegram\u002Femail alerts, enable 2FA and fix silent agents, TLS errors and blocked ports.","2026-02-14T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[210],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fself-host-beszel-vps-poster.svg",{"categorySlug":213,"appSlug":214},"monitoring-observability","beszel",1793145979274]