infradrift
Detect infrastructure drift between a known-good baseline and the current state of any Linux server — packages, listening ports, user accounts, privileged groups, cron jobs and systemd services.
Take a snapshot when the server is in a good state. Run check any time — after a deploy, from cron,
or during an incident — to see exactly what changed. No agent, no daemon, no dependencies on the server
beyond Python.
📄 Example
╔═══════════════════════════════╗
║ infradrift · drift report ║
╚═══════════════════════════════╝
Baseline 2026-05-10T09:00:00+02:00 (prod-01) as root
Current 2026-05-12T14:22:18+02:00 (prod-01) as root
── Ports ─────────────────────────────────
⛔ + 4444/tcp on all interfaces (nc) — new listening port
⛔ ~ 6379/tcp on all interfaces (redis-server) — now listening on a new address
── Users ─────────────────────────────────
⛔ ~ backup — account changed shell /usr/sbin/nologin → /bin/bash
⛔ + deploy — new user uid=1005 shell=/bin/bash
── Privileged Groups ─────────────────────
⛔ + deploy added to privileged group sudo
── Cron Jobs ─────────────────────────────
⚠ + New cron job: @reboot /tmp/.cache/update.sh crontab:www-data
── Services ──────────────────────────────
⚠ ~ nginx active → failed
── Packages ──────────────────────────────
· ~ [apt] openssl upgraded 3.0.13-0ubuntu3 → 3.0.13-0ubuntu3.1
──────────────────────────────────────────
8 changes detected (5 critical, 2 warnings, 1 info)
✨ What it detects
| Category | What's tracked |
|---|---|
| Packages | dpkg (Debian/Ubuntu), rpm (RHEL/Fedora/SUSE), pacman (Arch), pip — installed, removed, upgraded, downgraded |
| Ports | Listening TCP and bound UDP sockets with their bind address — a service moving from 127.0.0.1 to 0.0.0.0 is flagged |
| Users | /etc/passwd accounts — added, removed, uid/gid/shell/home changes |
| Privileged groups | Membership of sudo, wheel, admin, docker, lxd, libvirt, adm, disk, shadow, root |
| Cron jobs | User crontabs, /etc/crontab, /etc/cron.d/, /etc/cron.{hourly,daily,weekly,monthly}/, including @reboot entries |
| Services | systemd unit state (active / inactive / failed) and boot enablement (enabled / disabled / masked) |
Severity levels
| Severity | Examples |
|---|---|
| ⛔ CRITICAL | New listening port, port exposed on a new address, new user, uid/shell change, user added to a privileged group |
| ⚠️ WARNING | Service state or enablement change, new enabled service, cron job added/removed, package removed or downgraded |
| ℹ️ INFO | Package installed or upgraded, new high UDP port (ephemeral range — usually a client socket) |
📥 Installation
pip install infradrift
Run it as root for complete results: other users' crontabs and the process names of every socket are only visible to root. infradrift warns you when it runs unprivileged.
🛠 Usage
1 — Take a baseline snapshot
sudo infradrift snapshot
Saved to /var/lib/infradrift/baseline.json when run as root, or ~/.config/infradrift/baseline.json otherwise.
The file is created with 0600 permissions: it lists users, ports and cron commands.
sudo infradrift snapshot --output /opt/baselines/prod-$(date +%F).json
2 — Check for drift
sudo infradrift check # terminal report
sudo infradrift check --format json | jq .summary # JSON on stdout, progress on stderr
sudo infradrift check -f markdown -o drift-report.md # Markdown file
sudo infradrift check --baseline /opt/baselines/prod-2026-05-12.json --fail-on critical
3 — Diff any two snapshots
infradrift diff before-deploy.json after-deploy.json
infradrift diff web-01.json web-02.json --format markdown # compare two servers
🔁 Use in cron / CI
| Exit code | Meaning |
|---|---|
0 |
No drift at or above --fail-on |
1 |
Drift detected |
2 |
Error (missing or unreadable baseline) |
# Every hour: keep a Markdown report only when something critical changed
0 * * * * root infradrift check --fail-on critical -o /var/log/infradrift/last.md || cp /var/log/infradrift/last.md /var/log/infradrift/alert-$(date +\%F-\%H).md
📋 Options
infradrift snapshot
| Option | Description |
|---|---|
-o, --output FILE |
Where to save the baseline |
infradrift check / infradrift diff BASELINE CURRENT
| Option | Description |
|---|---|
-b, --baseline FILE |
(check) Baseline to compare against |
-f, --format |
terminal (default), json or markdown |
-o, --output FILE |
Write the report to a file (JSON or Markdown) |
--fail-on LEVEL |
info (default: any change), warning, critical or never |
🧪 Development
pip install -e ".[dev]"
ruff check .
pytest
See CHANGELOG.md for release notes.
📝 License
MIT — see LICENSE.
🌐 Connect
Release files for infradrift 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| infradrift-1.1.0.tar.gz | 22.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| infradrift-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.7 kB
Release files / infradrift-1.1.0.tar.gz
| Download URL | infradrift-1.1.0.tar.gz |
|---|---|
| Size | 22.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc642116e4680801987ee35dc6a27c27bf2aec2da984677a2a02cf44b8d3664c
|
|
BLAKE2b-256 checksum How to use checksums |
dce6e2abc99185a908ee7a6de804c1313503c2a1b682e89e3aafd1c84cdfc249
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / infradrift-1.1.0-py3-none-any.whl
| Download URL | infradrift-1.1.0-py3-none-any.whl |
|---|---|
| Size | 19.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f78371c65888b20f1a680f844ebb1aa87258335409270fd7c1d2fbe6ef5230bb
|
|
BLAKE2b-256 checksum How to use checksums |
2e5bbf5db21de07524baaee63aeb9d67c46a6c83ecc7dc153cf074e936d06514
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|