Real-time file system monitoring is essential for security auditing, configuration management, and application observability on Linux servers. Three primary kernel interfaces enable file event monitoring: fanotify, inotify, and the Go-native fsnotify library. This guide compares these monitoring approaches, their use cases, and how to deploy self-hosted file monitoring solutions.

Why Linux File System Monitoring Matters

Linux servers generate constant file system activity — configuration changes, log writes, application deployments, and potential unauthorized modifications. Without real-time monitoring, you discover problems only during periodic audits or when they cause visible failures.

File system monitoring enables:

  1. Security auditing — Detect unauthorized file modifications, rootkit installations, and configuration drift
  2. Configuration management — Trigger automatic reloads when config files change
  3. Application observability — Monitor file access patterns for performance analysis
  4. Compliance tracking — Maintain audit trails for regulatory requirements

For broader Linux security auditing, see our server security comparison and file integrity monitoring guide.

fanotify: File Access Notification System

fanotify (file access notification) is a modern Linux kernel interface introduced in kernel 2.6.37 that provides file system event monitoring at the file level (not directory level like inotify). It is designed for security and anti-virus applications that need to monitor file access across the entire file system.

Key Features

  • File-level monitoring — Monitor individual files, not just directories
  • Filesystem-wide events — Watch all files on a mounted filesystem, not just specific paths
  • Permission decisions — Block or allow file operations in real time (FAN_RESPONSE)
  • File descriptor access — Receive an open file descriptor with each event for content inspection
  • Fan groups — Create multiple independent monitoring groups with different rules
  • Mark types — Support for mount marks, filesystem marks, and directory marks

Kernel Interface

fanotify is accessed via the fanotify_init() and fanotify_mark() system calls:

1
2
3
4
5
6
#include <sys/fanotify.h>

int fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT, O_RDONLY);
fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
              FAN_OPEN | FAN_MODIFY | FAN_CLOSE_WRITE,
              AT_FDCWD, "/");

User-Space Tools

Several user-space tools leverage fanotify for practical file monitoring:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# fatrace — file access trace (uses fanotify internally)
sudo apt install fatrace
sudo fatrace -f CWO  # Monitor Close-Write and Open events

# SystemD Path units (uses inotify/fanotify)
# /etc/systemd/system/config-watch.path
[Path]
PathChanged=/etc/myapp/config.yaml

[Install]
WantedBy=multi-user.target

Docker Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
version: "3.8"
services:
  fatrace-monitor:
    image: alpine:latest
    privileged: true
    volumes:
      - /:/host:ro
      - /var/run/fatrace.sock:/var/run/fatrace.sock
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        apk add --no-cache fatrace
        fatrace -f CWO --mount=/host > /var/log/fatrace.log
    restart: unless-stopped

  fanotify-audit:
    image: alpine:latest
    privileged: true
    volumes:
      - /etc:/host-etc:ro
      - /var/log/audit:/var/log/audit
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        apk add --no-cache inotify-tools
        inotifywait -m -r -e modify,create,delete /host-etc \
          --format '%T %w %f %e' --timefmt '%Y-%m-%d %H:%M:%S' \
          >> /var/log/audit/config-changes.log
    restart: unless-stopped

When to Use fanotify

  • System-wide file access monitoring — Track all file opens, reads, and writes
  • Security and anti-virus scanning — Inspect files before they are executed
  • File operation blocking — Prevent unauthorized file modifications in real time
  • Mount-level monitoring — Watch entire filesystems without enumerating directories

inotify: Inode-Based File Notification

inotify (inode notify) is the original Linux file notification interface, available since kernel 2.6.13. It monitors specific directories and files for events like modifications, creations, deletions, and attribute changes.

Key Features

  • Directory-level monitoring — Watch specific directories and their contents
  • Granular event types — Access, modify, create, delete, move, close, open, and attribute changes
  • Recursive watching — Monitor directory trees (with limitations)
  • Well-established ecosystem — Widely supported by user-space tools and libraries
  • Resource limits — Controlled via fs.inotify.max_user_watches sysctl

User-Space Tools

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# inotifywait — wait for file events
sudo apt install inotify-tools
inotifywait -m -r -e modify,create,delete /etc/nginx/ \
  --format '%T %w %f %e' --timefmt '%Y-%m-%d %H:%M:%S'

# incron — inotify-based cron
sudo apt install incron
# Add to incrontab:
# /etc/nginx/nginx.conf IN_MODIFY /usr/sbin/nginx -t && /usr/sbin/nginx -s reload

# systemd.path — systemd's file monitoring
systemctl start config-watch.path
systemctl enable config-watch.path

SystemD Integration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# /etc/systemd/system/app-reload.path
[Unit]
Description=Watch application config for changes

[Path]
PathChanged=/opt/myapp/config.yaml
PathModified=/opt/myapp/config.yaml

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
# /etc/systemd/system/app-reload.service
[Unit]
Description=Reload application when config changes

[Service]
Type=oneshot
ExecStart=/opt/myapp/bin/reload-config

Docker Compose for inotify-Based Monitoring

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
version: "3.8"
services:
  config-watcher:
    image: alpine:latest
    volumes:
      - ./configs:/etc/myapp:ro
      - /var/log/myapp:/var/log/myapp
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        apk add --no-cache inotify-tools
        inotifywait -m -e modify,create,delete /etc/myapp \
          --format '%T %e %f' --timefmt '%F %T' \
          >> /var/log/myapp/config-audit.log
    restart: unless-stopped

  auto-reloader:
    image: alpine:latest
    volumes:
      - ./configs:/etc/myapp:ro
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        apk add --no-cache inotify-tools
        while inotifywait -e modify /etc/myapp/config.yaml; do
          echo "Config changed at $(date)"
          # Trigger application reload
        done
    restart: unless-stopped

When to Use inotify

  • Directory-specific monitoring — Watch known configuration directories
  • Application config reloads — Trigger service restarts when configs change
  • Build system integration — Rebuild or reprocess when source files change
  • Limited resource environments — Lower overhead than fanotify for targeted monitoring

fsnotify: Go-Native Cross-Platform File Watching

fsnotify (github.com/fsnotify/fsnotify) is the de facto standard Go library for cross-platform file system notification. It wraps inotify on Linux, FSEvents on macOS, and ReadDirectoryChangesW on Windows, providing a unified API.

Key Features

  • Cross-platform — Single API works on Linux, macOS, Windows, and BSD
  • Go-native — No CGo dependencies, pure Go implementation
  • Event channel API — Events delivered via Go channels for concurrent processing
  • Recursive watching — Add watchers for subdirectories dynamically
  • Widely adopted — Used by Hugo, Docker, Kubernetes, and many other Go projects

Go Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
    "log"
    "github.com/fsnotify/fsnotify"
)

func main() {
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    // Add directory to watch
    err = watcher.Add("/etc/myapp")
    if err != nil {
        log.Fatal(err)
    }

    // Process events
    for {
        select {
        case event, ok := <-watcher.Events:
            if !ok {
                return
            }
            if event.Op&fsnotify.Write == fsnotify.Write {
                log.Printf("Modified: %s", event.Name)
            }
            if event.Op&fsnotify.Create == fsnotify.Create {
                log.Printf("Created: %s", event.Name)
            }
        case err, ok := <-watcher.Errors:
            if !ok {
                return
            }
            log.Printf("Error: %v", err)
        }
    }
}

When to Use fsnotify

  • Go application development — Native file watching in Go services
  • Cross-platform tools — Single codebase for Linux, macOS, and Windows
  • Build tools and dev servers — Hugo uses fsnotify for live reload
  • Lightweight monitoring — Simple event processing without kernel-level features

Comparison Table

Featurefanotifyinotifyfsnotify
Kernel Interfacefanotify (2.6.37+)inotify (2.6.13+)Wraps inotify (Linux)
Monitoring ScopeFilesystem-wideDirectory-specificDirectory-specific
File Descriptor AccessYes (open fd with events)NoNo
Permission BlockingYes (FAN_RESPONSE)NoNo
Cross-PlatformLinux onlyLinux onlyLinux, macOS, Windows, BSD
Recursive WatchingYes (filesystem mark)Manual per-directoryManual per-directory
Resource LimitsNo hard limitmax_user_watches sysctlLimited by inotify
Security Use CasesAnti-virus, auditingConfig monitoringApp-level watching
Language SupportC, Python wrappersC, CLI tools, PythonGo
Best ForSystem-wide securityConfig reloads, auditingGo applications, dev tools

Choosing the Right Approach

For Security Auditing

Use fanotify for comprehensive file access monitoring across the entire filesystem. Its file descriptor access and permission blocking capabilities make it suitable for security-sensitive environments.

For Configuration Management

Use inotify (via inotify-tools or systemd.path) for monitoring specific configuration directories. It is lightweight, well-understood, and integrates cleanly with system services.

For Go Application Development

Use fsnotify for cross-platform file watching in Go applications. Its channel-based API integrates naturally with Go’s concurrency model.

1
2
3
4
5
fanotify (filesystem-wide security)  Log to audit system
     +
inotify (config directory monitoring)  Trigger service reloads
     +
fsnotify (application-level watching)  In-app file processing

Why Self-Host File Monitoring?

Running file monitoring on your own Linux servers ensures complete visibility into file system activity, avoids sending sensitive file paths to external services, and integrates directly with local security tools. For compliance-driven environments, self-hosted monitoring keeps audit trails under your control and enables real-time response to unauthorized changes.

For Linux system administration tooling, our systemd timer management guide covers scheduling periodic file integrity checks. Teams managing Linux security should also review our SELinux management comparison for mandatory access control enforcement.

FAQ

What is the difference between fanotify and inotify?

fanotify monitors at the file level across entire filesystems and can provide open file descriptors with events, enabling content inspection and permission decisions. inotify monitors specific directories and files, providing event notifications without file access. fanotify is designed for security applications; inotify is designed for application-level file watching.

Can fanotify monitor all files on a system without impacting performance?

fanotify’s filesystem-wide monitoring does introduce overhead proportional to file access volume. For high-throughput systems, filtering events (e.g., only monitoring specific file types or excluding high-churn directories like /tmp) is recommended. The FAN_REPORT_FID flag reduces overhead by reporting file handles instead of paths.

How many files can inotify watch simultaneously?

The default limit is typically 8,192 watches, controlled by fs.inotify.max_user_watches. For monitoring large directory trees, increase this value: sysctl fs.inotify.max_user_watches=524288. Each watch consumes approximately 1KB of kernel memory.

Does fsnotify work on Linux kernels older than 2.6.13?

No. fsnotify wraps inotify on Linux, which requires kernel 2.6.13 or later. All modern Linux distributions ship with kernels far newer than this (5.x or 6.x series), so compatibility is not a concern for current systems.

How do I make file monitoring survive system reboots?

For inotify-based monitoring, use systemd.path units which are managed by systemd and automatically restart on boot. For fanotify tools, create a systemd service unit with Restart=always. For fsnotify-based Go applications, run them as systemd services or in Docker containers with restart policies.

Can I use these tools to monitor Docker container file systems?

Yes, but with caveats. inotify and fanotify work on the host filesystem, so you can monitor Docker volume mount points. For monitoring inside containers, install inotify-tools within the container image. Note that overlay filesystems may have limitations with inotify event delivery for some operations.