Python Disk Usage Monitoring — Core Concepts

Three ways to check disk usage in Python

shutil.disk_usage — the simplest option

import shutil

usage = shutil.disk_usage('/')
print(f"Total: {usage.total / (1024**3):.1f} GB")
print(f"Used:  {usage.used / (1024**3):.1f} GB")
print(f"Free:  {usage.free / (1024**3):.1f} GB")
print(f"Usage: {usage.used / usage.total * 100:.1f}%")

shutil.disk_usage() is part of Python’s standard library. No extra packages needed. It returns a named tuple with total, used, and free in bytes.

os.statvfs — more detailed filesystem info

import os

stat = os.statvfs('/')
total = stat.f_blocks * stat.f_frsize
free = stat.f_bavail * stat.f_frsize   # Available to non-root users
used = total - (stat.f_bfree * stat.f_frsize)
inode_total = stat.f_files
inode_free = stat.f_favail

The key distinction: f_bfree is the total free space (including space reserved for root), while f_bavail is space available to regular users. On most Linux systems, 5% of disk space is reserved for root, so a disk that looks 95% full to a normal user still has space for system operations.

psutil — cross-platform with partition info

import psutil

for partition in psutil.disk_partitions():
    try:
        usage = psutil.disk_usage(partition.mountpoint)
        print(f"{partition.mountpoint}: {usage.percent}% used "
              f"({usage.free / (1024**3):.1f} GB free)")
    except PermissionError:
        continue

psutil also discovers all mounted partitions, giving you a complete picture of every filesystem on the machine.

Building a basic disk monitor

import shutil
from dataclasses import dataclass

@dataclass
class DiskAlert:
    path: str
    percent_used: float
    free_gb: float
    level: str  # 'warning' or 'critical'

def check_disk_usage(paths: list[str], warn_at=80, critical_at=90) -> list[DiskAlert]:
    alerts = []
    for path in paths:
        usage = shutil.disk_usage(path)
        percent = usage.used / usage.total * 100
        free_gb = usage.free / (1024**3)

        if percent >= critical_at:
            alerts.append(DiskAlert(path, percent, free_gb, 'critical'))
        elif percent >= warn_at:
            alerts.append(DiskAlert(path, percent, free_gb, 'warning'))

    return alerts

# Check common paths
alerts = check_disk_usage(['/', '/home', '/var/log', '/tmp'])
for alert in alerts:
    print(f"[{alert.level.upper()}] {alert.path}: "
          f"{alert.percent_used:.1f}% used, {alert.free_gb:.1f} GB free")

Tracking what is using the space

When a disk is full, the next question is: what is taking up all the space? Python can answer this:

from pathlib import Path

def directory_sizes(root: str, depth: int = 1) -> list[tuple[str, int]]:
    """Get sizes of directories at a given depth."""
    root_path = Path(root)
    sizes = []

    for item in sorted(root_path.iterdir()):
        if item.is_dir() and not item.is_symlink():
            try:
                total = sum(
                    f.stat().st_size
                    for f in item.rglob('*')
                    if f.is_file() and not f.is_symlink()
                )
                sizes.append((str(item), total))
            except PermissionError:
                continue

    return sorted(sizes, key=lambda x: x[1], reverse=True)

# Find what is eating space in /var
for path, size in directory_sizes('/var')[:10]:
    print(f"{size / (1024**3):.2f} GB  {path}")

Growth trend detection

A single check tells you the current state. But knowing that a disk is filling up at 2 GB per day — and will be full in 5 days — is far more actionable:

import json
import time
from pathlib import Path

HISTORY_FILE = Path('/var/lib/disk-monitor/history.json')

def record_usage(path: str):
    usage = shutil.disk_usage(path)
    entry = {
        'timestamp': time.time(),
        'used_bytes': usage.used,
        'total_bytes': usage.total,
    }

    history = []
    if HISTORY_FILE.exists():
        history = json.loads(HISTORY_FILE.read_text())

    history.append(entry)
    # Keep 30 days of hourly samples
    history = history[-720:]
    HISTORY_FILE.write_text(json.dumps(history))
    return history

def predict_days_until_full(history: list[dict]) -> float | None:
    if len(history) < 2:
        return None

    first = history[0]
    last = history[-1]
    elapsed_days = (last['timestamp'] - first['timestamp']) / 86400
    growth_bytes = last['used_bytes'] - first['used_bytes']

    if growth_bytes <= 0 or elapsed_days <= 0:
        return None  # Disk is not growing

    growth_per_day = growth_bytes / elapsed_days
    remaining_bytes = last['total_bytes'] - last['used_bytes']
    return remaining_bytes / growth_per_day

Common misconception

Many people only monitor percentage used. But 90% of a 50 GB disk (5 GB free) is very different from 90% of a 2 TB disk (200 GB free). Always consider both the percentage and the absolute free space. A server with 200 GB free is probably fine even at 90%, while a server with 500 MB free at 75% is in trouble.

Inode exhaustion

Disk space is not the only resource that can run out. Every file and directory uses an inode — a metadata entry in the filesystem. It is possible to have plenty of free space but run out of inodes, especially on systems with millions of small files (like mail servers or caching systems):

import os

stat = os.statvfs('/')
inode_percent = (1 - stat.f_favail / stat.f_files) * 100
print(f"Inode usage: {inode_percent:.1f}%")

Practical use cases

  • Server monitoring scripts that check all filesystems and send alerts via email or Slack
  • CI/CD pipelines that verify sufficient disk space before starting builds
  • Database servers that track data directory growth and predict when storage needs expansion
  • Log monitoring that identifies when log directories are growing faster than rotation can clean up
  • Backup verification that confirms backup destinations have room for the next scheduled backup

One thing to remember: Effective disk monitoring goes beyond checking a percentage — it tracks growth rates to predict future problems, checks inode usage alongside space, and examines which directories are consuming the most storage to guide cleanup decisions.

pythonmonitoringsystem-administrationstorage

See Also