FUTIA
GÜVENLIK8 min read

Linux File Integrity Monitoring: Bash + Telegram Alert System

You can be notified via Telegram within 30 seconds when critical files on your server change. A simple but effective solution with Bash script + inotify.

Linux File Integrity Monitoring: Bash + Telegram Alert System
Miraç Eroğlu
May 8, 2026

How long does it take you to notice when a critical file changes on your server? An hour? A day? Or do you never notice at all? In 2023, while processing data pulled from the ilan.gov.tr API on kamupersonelhaber.com, I discovered that someone had modified the .env file—6 hours later. We had rotated the API key, and someone had manually written back the old key. For 6 hours, our system tried to make requests with an old, invalid key. Since that day, I've set up an automatic monitoring system for critical files. Bash script + inotify + Telegram. A total of 87 lines of code. Whenever any file changes, I get a notification on my phone within 30 seconds. It's currently running on all of FUTIA's production servers. In this post, I'll show you step-by-step how to set up the same system, which files you should monitor, and the Telegram integration.

Why Is File Integrity Monitoring Important?

Most small-to-medium scale projects handle server security with firewall + SSH key. Is it enough? No. Someone who gains access to your server (authorized or unauthorized) can modify critical files. Scenarios:

  • A team member accidentally confuses the production .env file with staging
  • An attacker obtains an SSH key and adds a backdoor to a cron job
  • An automatic update system breaks nginx.conf
  • When a Docker container restarts, it loads an old config file due to incorrect volume mount configuration

I've experienced all of these scenarios. The worst part: not noticing. The system runs incorrectly silently, and you try to figure out why. File integrity monitoring makes these silent errors loud. When a file changes, you're immediately notified. If it's a manual change, you say "okay, that was me." If not, you intervene immediately.

Files we monitor at FUTIA:

  • .env (API keys, database credentials)
  • nginx.conf and site-specific config files
  • crontab files
  • systemd service files
  • Docker Compose YAMLs
  • SSH authorized_keys
  • /etc/hosts (for DNS poisoning detection)

When any of these files change, I get a Telegram notification. If I'm expecting the change, I ignore it. If not, I connect to the server and check the logs.

What Is inotify and How Does It Work?

inotify is an API provided by the Linux kernel for monitoring file system events. When a change occurs on a file or directory (create, modify, delete, move), the kernel sends an event to userspace. You don't need to poll; you're notified when an event occurs.

To use inotify in Bash, you need the inotify-tools package:

# Ubuntu/Debian
sudo apt install inotify-tools

# CentOS/RHEL
sudo yum install inotify-tools

Basic usage:

inotifywait -m -e modify,create,delete /path/to/file
  • -m: monitor mode, continuous monitoring (not one-time)
  • -e: event types (modify, create, delete, move, attrib)
  • Last parameter: file or directory to monitor

Example output:

/home/user/.env MODIFY
/home/user/.env ATTRIB

We can capture this output in a bash script and send it to Telegram.

Increasing inotify Limits

Default inotify limits are sufficient for small projects, but if you're monitoring many files, you may encounter limits. To check:

cat /proc/sys/fs/inotify/max_user_watches

Default is usually 8,192. To increase:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

I use 524,288 and have never hit the limit.

Telegram Bot Setup

To send notifications to Telegram, you need to create a bot and get your chat ID.

1. Open @BotFather on Telegram 2. Send the /newbot command 3. Give your bot a name (e.g., "Server Monitoring Bot") 4. Give it a unique username (e.g., "myserver_monitor_bot") 5. BotFather will give you a token (e.g., 123456789:ABCdefGHIjklMNOpqrsTUVwxyz)

Now find your chat ID:

1. Send any message to the bot you created 2. Open this URL in your browser: https://api.telegram.org/bot<TOKEN>/getUpdates 3. In the JSON response, you'll see your chat ID as "chat":{"id":123456789}

Note these two values (token and chat ID). We'll use them in the script.

Bash Script: File Monitoring + Telegram Alert

Here's the fully working script. You can save it as /usr/local/bin/file-monitor.sh:

#!/bin/bash

# Telegram config
TELEGRAM_BOT_TOKEN="123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
TELEGRAM_CHAT_ID="123456789"

# Files to watch (one file per line)
WATCH_FILES=(
    "/home/user/.env"
    "/etc/nginx/nginx.conf"
    "/etc/nginx/sites-enabled/mysite.conf"
    "/var/spool/cron/crontabs/root"
    "/etc/systemd/system/myapp.service"
    "/home/user/docker-compose.yml"
    "/root/.ssh/authorized_keys"
)

# Telegram message sending function
send_telegram() {
    local message="$1"
    curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
        -d chat_id="${TELEGRAM_CHAT_ID}" \
        -d text="${message}" \
        -d parse_mode="HTML" > /dev/null
}

# Get server hostname
HOSTNAME=$(hostname)

# Notify when script starts
send_telegram "🟢 <b>File monitoring started</b>\n\nServer: ${HOSTNAME}\nFiles monitored: ${#WATCH_FILES[@]}"

# Start inotifywait for each file
for file in "${WATCH_FILES[@]}"; do
    if [ ! -e "$file" ]; then
        send_telegram "⚠️ <b>Warning</b>\n\nFile not found: ${file}\nServer: ${HOSTNAME}"
        continue
    fi
    
    # Start monitoring in background
    inotifywait -m -e modify,create,delete,move,attrib "$file" 2>/dev/null | while read path action file_name; do
        # Prepare change information
        timestamp=$(date '+%Y-%m-%d %H:%M:%S')
        
        # Last 5 lines of log (if exists)
        if [ -f "$path" ]; then
            last_lines=$(tail -n 5 "$path" 2>/dev/null | sed 's/</\&lt;/g; s/>/\&gt;/g')
        else
            last_lines="(File deleted)"
        fi
        
        # Telegram message
        message="🔴 <b>FILE CHANGE DETECTED</b>\n\n"
        message+="📁 File: ${path}\n"
        message+="⚡ Action: ${action}\n"
        message+="🖥 Server: ${HOSTNAME}\n"
        message+="🕐 Time: ${timestamp}\n\n"
        message+="📄 Last 5 lines:\n<code>${last_lines}</code>"
        
        send_telegram "${message}"
    done &
done

# Notify if script terminates
trap 'send_telegram "🔴 <b>File monitoring stopped</b>\n\nServer: ${HOSTNAME}"' EXIT

# Infinite loop (to keep script running)
while true; do
    sleep 3600
done

What the script does:

1. Monitors specified files with inotify 2. Sends a Telegram message when any change occurs 3. Message includes file path, action type, server name, and last 5 lines 4. Sends notifications when script starts and stops 5. Warns if a monitored file doesn't exist

Make the script executable:

chmod +x /usr/local/bin/file-monitor.sh

Running as a Systemd Service

Create a systemd service for the script to run continuously. /etc/systemd/system/file-monitor.service:

[Unit]
Description=File Integrity Monitor with Telegram Alerts
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/file-monitor.sh
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Activate the service:

sudo systemctl daemon-reload
sudo systemctl enable file-monitor.service
sudo systemctl start file-monitor.service

Status check:

sudo systemctl status file-monitor.service

To view logs:

journalctl -u file-monitor.service -f

Real-World Scenario: kamupersonelhaber.com

On kamupersonelhaber.com, we automatically publish 50+ public job postings daily. Job data comes from the ilan.gov.tr API. The API key is in the .env file. One day, I noticed the system wasn't pulling postings. I checked the logs: 401 Unauthorized. We had rotated the API key, and I had written the new key to .env. But someone (from the team) had restored the old .env from backup. Manual intervention. The new key was lost, the old key was invalid.

If we had the file monitoring system: 1. I would have received a Telegram notification when .env changed 2. I would have said "I didn't change that" 3. I would have checked immediately 4. I would have fixed it within 5 minutes

In reality: I noticed 6 hours later. 6 hours of data loss. Since that day, I've installed file monitoring on all production servers.

Advanced Features

Hash-Based Change Detection

inotify triggers on every change. But sometimes the file doesn't change, it's just touched or metadata is updated. To detect actual content changes, you can use hashes:

# Store file hash
OLD_HASH=$(md5sum "$file" | awk '{print $1}')

# Inside monitoring loop
NEW_HASH=$(md5sum "$file" | awk '{print $1}')
if [ "$OLD_HASH" != "$NEW_HASH" ]; then
    send_telegram "Hash changed: $file"
    OLD_HASH="$NEW_HASH"
fi

This approach reduces false positives.

Directory Monitoring

You can monitor an entire directory instead of a single file:

inotifywait -m -r -e modify,create,delete /etc/nginx/

The -r parameter enables recursive monitoring. But be careful: if too many events come in, you might hit Telegram's rate limit. In this case, group events and send a summary once per minute.

Slack or Discord Integration

If you want to use Slack or Discord instead of Telegram, you can use webhook URLs:

# Slack
curl -X POST -H 'Content-type: application/json' \
    --data '{"text":"File changed: /etc/nginx/nginx.conf"}' \
    YOUR_SLACK_WEBHOOK_URL

# Discord
curl -X POST -H 'Content-type: application/json' \
    --data '{"content":"File changed: /etc/nginx/nginx.conf"}' \
    YOUR_DISCORD_WEBHOOK_URL

I prefer Telegram because the bot API is more flexible and rate limits are higher.

Performance and Resource Usage

inotify is very lightweight. On FUTIA servers, I monitor 15-20 files, CPU usage is below 0.1%, RAM usage is 5-10 MB. Since it's a kernel-level mechanism, it's much more efficient than polling solutions.

Things to watch out for:

  • Increase max_user_watches limit when monitoring many files
  • Don't monitor high-frequency changing files (like log files), you'll hit Telegram's rate limit
  • If monitored files are deleted and recreated, inotify may lose the watch, restart the script

Alternative Tools

Besides bash scripts, there are other solutions:

AIDE (Advanced Intrusion Detection Environment): Industry-standard tool for file integrity checking. Hash-based, performs periodic scans. But not real-time, runs via cron job.

Tripwire: Commercial, enterprise-grade file integrity monitoring. Very powerful but overkill for small projects.

Osquery: Facebook's open-source tool. You query server state with SQL. Can also monitor file changes. But setup and configuration are complex.

Auditd: Linux audit subsystem. Kernel-level, very detailed logs. But log volume is high, analysis is difficult.

I prefer the simple bash script because:

  • 5-minute setup
  • Understandable, customizable
  • Minimal dependencies (inotify-tools + curl)
  • Real-time notifications

For enterprise environments, I would use AIDE or Tripwire. For small-to-medium scale projects, a bash script is sufficient.

Security Notes

File monitoring is one layer of security, not sufficient on its own. Additional measures:

  • Use SSH key-based authentication, disable password authentication
  • Limit sudo privileges, don't give root to every user
  • Keep firewall rules tight, only open necessary ports
  • Enable automatic security updates
  • Back up critical files (keep in git repo or upload to S3)

File monitoring is an early warning system added on top of these layers. It helps you notice quickly when something goes wrong.

Usage at FUTIA

This system runs on all of FUTIA's production servers. On VPS servers in the Netherlands, on customer servers in Turkey. I receive an average of 3-4 real alerts per month. Most are expected changes (me or a team member updating configs). But last month, something strange happened: nginx.conf changed on the diolivo.com.tr server. I didn't change it, the customer didn't change it. I connected to the server and checked the logs. The automatic update system had written the default config file while updating nginx. I restored from backup, 10 minutes of downtime. Without file monitoring, I wouldn't have noticed until the customer said "site isn't loading."

Another example: the crontab file changed on kamupersonelhaber.com. I got a Telegram notification and checked. A new cron job was added: take a database dump every night at 3 AM and upload to S3. I had added it and forgotten. Thanks to the notification, I remembered and verified it was working correctly.

Tracking these small but critical changes improves system reliability.

If you manage Linux servers, I recommend setting up file integrity monitoring. 1 hour of setup saves a lot of time in the long run. At FUTIA, we also install this system on our customers' servers. If you want a custom monitoring or automation solution for your own server, you can contact me: WhatsApp +90 532 491 17 05 or info@futia.net. We can also set up much more advanced, Python-based, multi-server supported systems beyond bash scripts.

Frequently Asked Questions

Can't I just do periodic checks with a cron job instead of inotify?

You can, but it won't be real-time. Even if the cron job runs every 5 minutes, you'll notice the change 5 minutes later. inotify triggers instantly, you get a notification within 30 seconds. Also, cron jobs read files and calculate hashes every time, using CPU and disk I/O. inotify is kernel-level, much more efficient. Real-time monitoring is essential for critical files, cron jobs can be used as a backup solution.

Will I hit Telegram's rate limit?

Not with normal usage. Telegram bot API limit is 30 messages per second, 20 different chats per minute. File monitoring systems don't send messages frequently, only when changes occur. But if you monitor high-frequency changing files (like log files), you might hit the rate limit. In that case, group messages: send a summary once per minute, don't send a separate message for each change. I've been using it for 2 years and never experienced rate limit issues.

Which files should I monitor?

Monitor critical configuration files: .env (API keys, credentials), web server config (nginx.conf, apache2.conf), crontab files, systemd service files, Docker Compose YAMLs, SSH authorized_keys. Also, /etc/hosts (DNS poisoning detection), /etc/passwd and /etc/shadow (user changes) can be monitored. Don't monitor log files, too many events come in. Don't monitor application code, it's already versioned with git. Only monitor files that shouldn't be manually changed in production.

What happens if the script stops?

If you run it as a systemd service, it will automatically restart. We configured Restart=always and RestartSec=10 in the script, if it stops, it restarts after 10 seconds. Also, the script sends a Telegram notification when it stops via EXIT trap, so you'll notice. If the server reboots, the systemd service starts automatically (we activated it with enable). Still, I recommend checking with systemctl status once a week.

How do I use this on multiple servers?

You can install a separate script on each server, using the same Telegram bot. Since the server hostname is in the messages, you'll know which server it came from. More advanced solution: monitor all servers from a central server. Run a lightweight agent on each server, send changes to the central server, send Telegram notifications from there. At FUTIA, we have 8 servers, each running a separate script, all sending messages to the same Telegram chat. No confusion, I distinguish them by hostname.

ABOUT THE AUTHOR
Miraç Eroğlu

Hacettepe mezunu, 6 yıldır sosyal medya, 2 yıldır AI otomasyon.

Learn more →

Want to apply one of the techniques from this post? Fill out a short form and we'll email you a free preview audit within 48 hours.