InfraRunBook
    Back to articles

    Setting Up File Integrity Monitoring With AIDE on Linux Servers

    Cyber Security for Servers
    Published: Aug 25, 2026
    Updated: Aug 25, 2026

    A practical, field-tested guide to installing, configuring, and tuning AIDE for file integrity monitoring on Linux servers, including a full config example and the mistakes that cause false alarms.

    Setting Up File Integrity Monitoring With AIDE on Linux Servers

    File integrity monitoring is one of those controls that everyone agrees is a good idea, right up until they have to actually maintain it. AIDE (Advanced Intrusion Detection Environment) is the tool I reach for on most Linux servers I manage, mostly because it's dependency-light, it's been around long enough to be boring in the good sense, and it doesn't phone home anywhere. In my experience, the failure mode with FIM tools isn't setting them up, it's that people set them up once, get flooded with false positives from routine package updates, and then quietly stop reading the reports. This guide walks through a setup that avoids that trap.

    Prerequisites

    Before you touch aide.conf, get a few things straight. You need root or sudo access on the server you're protecting, obviously, but you also need a plan for where the AIDE database itself will live once it's been generated. If an attacker can modify both your filesystem and your AIDE database, the whole exercise is theater. I usually copy the initial database off to a separate host, or at minimum onto read-only media, right after initialization.

    You'll also want a rough mental map of which directories on the server change frequently and legitimately — log directories, package caches, temp directories, container overlay filesystems if you're running Docker — because these are exactly the paths that generate noise if you don't exclude them properly. Spend ten minutes with

    df -h
    and
    mount
    to understand your filesystem layout first.

    Finally, confirm you have a package manager path to AIDE. On Debian-based distros it's in the default repos, on RHEL-based distros you'll likely need EPEL. Check with:

    $ apt-cache policy aide
    $ dnf info aide

    Step-by-step setup

    Start with installation. On a Debian/Ubuntu box:

    $ sudo apt update
    $ sudo apt install aide aide-common

    On RHEL/CentOS/Rocky:

    $ sudo dnf install epel-release
    $ sudo dnf install aide

    Once installed, the default config at

    /etc/aide/aide.conf
    (Debian) or
    /etc/aide.conf
    (RHEL) is a reasonable starting point but almost never what you want to ship to production untouched. I always back it up before editing, because I've been burned before by losing track of what the distro defaults actually were.

    $ sudo cp /etc/aide/aide.conf /etc/aide/aide.conf.orig

    Next, decide on your rule definitions. AIDE lets you define named groups of checks (permissions, ownership, hashes, size, timestamps) and then apply those groups selectively to paths. This is the part people skip, and it's the part that determines whether your alerts are useful six months from now. A rule like

    FIPSR
    checking full attributes on
    /etc
    makes sense. Applying that same aggressive rule to
    /var/log
    will generate a wall of noise every single night from logrotate.

    Edit the config to add your monitored paths and exclusions. I'll show the specifics in the full example below, but the workflow is: define what to watch, define what to explicitly ignore, then initialize the database.

    $ sudo aide --config=/etc/aide/aide.conf --init

    This can take a while on a server with a lot of files — I've seen it run for twenty minutes-plus on a file server with millions of small files. When it finishes, it writes a new database, usually to

    /var/lib/aide/aide.db.new.gz
    . You need to rename this to the active database before AIDE will use it for comparisons:

    $ sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

    Now copy that database somewhere outside the reach of anything that could compromise this server — an offline backup location, a separate management host, or write-once storage. On a host named sw-infrarunbook-01, I typically scp it to a dedicated audit host as part of the same maintenance window:

    $ scp /var/lib/aide/aide.db.gz infrarunbook-admin@10.20.4.15:/opt/aide-baselines/sw-infrarunbook-01-$(date +%Y%m%d).db.gz

    Last step for setup is scheduling regular checks. A nightly cron job is standard, though on higher-value hosts I'll run it twice a day.

    $ sudo crontab -e
    # Add the following line
    0 3 * * * /usr/sbin/aide --config=/etc/aide/aide.conf --check | mail -s "AIDE report: sw-infrarunbook-01" secops@solvethenetwork.com

    Full configuration example

    Here's a working aide.conf I'd actually deploy on a general-purpose Linux web/app server. Adjust the paths for your own environment, but this structure — database location, rule group definitions, then explicit path rules ordered from broad to narrow — is the pattern worth keeping.

    # /etc/aide/aide.conf
    
    # Where the baseline and new databases live
    database=file:/var/lib/aide/aide.db.gz
    database_out=file:/var/lib/aide/aide.db.new.gz
    
    # Compression for the database file
    gzip_dbout=yes
    
    # Verbosity and report format
    verbose=5
    report_url=file:/var/log/aide/aide.log
    report_url=stdout
    
    # Custom rule groups
    # p=permissions, i=inode, n=link count, u=user, g=group
    # s=size, m=mtime, c=ctime, S=growing size check, sha256=hash
    FIPSR = p+i+n+u+g+s+m+c+sha256
    DATAONLY = p+n+u+g+s+sha256
    LOGCHECK = p+u+g
    
    # System binaries and config -- watch everything
    /boot   FIPSR
    /bin    FIPSR
    /sbin   FIPSR
    /usr/bin FIPSR
    /usr/sbin FIPSR
    /usr/lib FIPSR
    /etc    FIPSR
    
    # SSH and sudo config get the strictest treatment
    /etc/ssh/sshd_config FIPSR
    /etc/sudoers FIPSR
    /etc/sudoers.d FIPSR
    
    # Application code, if this box hosts something specific
    /opt/solvethenetwork-app DATAONLY
    
    # Log directories -- ownership and permission checks only,
    # content and size change constantly and legitimately
    /var/log LOGCHECK
    
    # Explicit exclusions -- these paths churn by design
    !/var/log/journal
    !/var/lib/docker
    !/var/cache
    !/tmp
    !/var/tmp
    !/proc
    !/sys
    !/dev
    !/run
    !/var/lib/aide

    Notice the ordering: broad directory rules come first, more specific overrides for particular files come after, and exclusions come last. AIDE evaluates rules in the order they appear and the most specific matching rule wins, so if you put an exclusion before a broad watch rule that also matches, you can end up with unexpected coverage gaps. I've debugged that exact issue on a client's box where

    /etc/cron.d
    wasn't being monitored because an earlier, broader exclusion silently swallowed it.

    Verification steps

    Don't trust that the setup worked just because the commands didn't error. Verify it properly.

    First, confirm the database actually has entries and isn't some tiny placeholder file from a failed init:

    $ ls -lh /var/lib/aide/aide.db.gz
    $ zcat /var/lib/aide/aide.db.gz | wc -l

    A database with only a few hundred lines on a server with a full OS install is a sign something went wrong, usually an overly broad exclusion list.

    Second, run a manual check and confirm it completes cleanly against the fresh baseline, meaning it should report no changes:

    $ sudo aide --config=/etc/aide/aide.conf --check

    Third, and this is the step people skip, deliberately make a change and confirm AIDE catches it. Touch a file in a monitored path, change permissions on something in /etc, and re-run the check:

    $ sudo chmod 644 /etc/ssh/sshd_config
    $ sudo aide --config=/etc/aide/aide.conf --check

    You should see that permission change flagged clearly in the output, something like a Permissions entry showing the old and new mode. If you don't see it, your rule definitions for that path are weaker than you think. Revert the test change and re-run to confirm it goes clean again before moving on.

    Fourth, verify the cron job actually fires and the mail or log output reaches you. Don't wait for the 3am run to find out your mail transport agent isn't configured. Test it manually with the same command the cron job uses, piped the same way.

    Fifth, confirm the offline copy of the baseline database is retrievable and matches. A checksum comparison between the local and remote copy right after the scp takes thirty seconds and saves you from discovering a corrupted transfer weeks later.

    $ sha256sum /var/lib/aide/aide.db.gz
    $ ssh infrarunbook-admin@10.20.4.15 sha256sum /opt/aide-baselines/sw-infrarunbook-01-*.db.gz

    Common mistakes

    The single biggest mistake I see is applying the strictest rule group to every path uniformly. It feels thorough, but it guarantees alert fatigue within a week because log rotation, package manager caches, and session files all trip full-attribute checks constantly. Once a team starts ignoring AIDE emails because they're always full of expected noise, the control is effectively dead even though it's technically still running.

    A close second is forgetting to re-baseline after legitimate maintenance. You patch the OS, you deploy a new application release, and suddenly AIDE reports hundreds of changes. That's expected — but if you don't regenerate the database afterward, every subsequent check re-reports the same drift indefinitely, which trains whoever's reading the reports to skim past them. After any planned change window, re-run

    --init
    , verify the diff makes sense, then promote the new database.

    Storing the database on the same host it protects, with no offline copy, defeats the purpose of the tool. If an intruder has write access to the filesystem, they have write access to your baseline too, and can simply update it to hide their tracks before you ever run a check. I've seen this assumed to be "good enough" more than once, usually because copying the file off-host felt like an extra step nobody prioritized.

    Not excluding virtualization and container layers is another one. If you're running Docker or Podman on the host, the writable overlay filesystems under

    /var/lib/docker
    change constantly as containers run, and monitoring them with AIDE at the host level produces meaningless noise. Monitor container images and running container filesystems with a tool designed for that layer, and let AIDE focus on the host OS.

    People also forget to test the alerting path end-to-end. It's easy to set up the cron job, see the log file getting written locally, and assume the alert pipeline works. If the mail command silently fails because there's no MTA configured, you have a FIM tool that's quietly writing reports nobody sees. Test the full path from cron trigger to inbox before you consider this done.

    Last one: running

    --init
    without checking that the previous compromise, if any, isn't already baked into the "clean" baseline. If you're deploying AIDE on a server that's been running for a while without any integrity monitoring, that first baseline is only as trustworthy as the server's current state. On any server where you have even mild suspicion of prior compromise, do a manual review of critical paths — SSH keys, cron entries, setuid binaries — before you trust the initial database as your ground truth.

    Frequently Asked Questions

    How often should AIDE checks run on a production server?

    Nightly is the common baseline, but for higher-value hosts like authentication servers or hosts handling sensitive data, running checks twice a day gives you a tighter detection window without much added overhead.

    Does AIDE protect against an attacker who gets root access?

    Not on its own. If the AIDE database and binary live on the same compromised host, an attacker with root can regenerate or tamper with the baseline. Storing a copy of the database off-host is what actually preserves its value.

    Why does AIDE report so many false positives after a system update?

    Package upgrades legitimately change binaries, configs, and timestamps across the filesystem, so a check right after patching will show a large diff. Re-run --init after verifying the changes are expected, then promote the new database as your baseline.

    Can AIDE monitor files inside Docker containers?

    Not effectively at the host level. Container overlay filesystems change too frequently to be useful targets for AIDE's rule model. Exclude paths like /var/lib/docker from host-level monitoring and use a container-aware integrity or runtime security tool for that layer instead.

    What's the minimum set of directories worth monitoring if I can't cover the whole filesystem?

    At minimum, watch /etc, /bin, /sbin, /usr/bin, /usr/sbin, and /boot with full attribute checks, along with sudoers and SSH configuration specifically. These are the paths an attacker most needs to modify to establish persistence.

    Related Articles