Back to Blog
Python

Python psutil: CPU, Memory, Disk, and Network Usage

python psutil cpu memory disk and network usage: Learn how to use Python's psutil library to monitor CPU, memory, disk, and network usage with practical examples and p...

psutilsystem monitoringCPU usagememory usagenetwork monitoringdisk usage
Illustration of a Python script monitoring system resources including CPU, memory, disk, and network using psutil.

python psutil cpu memory disk and network usage requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need to report or react to system resource consumption from Python, psutil provides a consistent API for CPU, memory, disk, and network metrics. This article shows how to read each metric, what the return values mean, and how to avoid common pitfalls when polling them in a real application.

Reading CPU Usage with psutil

The cpu_percent() function is the most direct way to get CPU utilization. With no arguments, it returns the percentage since the last call, which makes the first call meaningless in practice. To get a meaningful reading, pass an interval in seconds so psutil samples the CPU over that window.

import psutil # Block for 1 second and return the average CPU usage cpu = psutil.cpu_percent(interval=1) print(f"CPU usage: {cpu}%")

If you need non-blocking behavior, call cpu_percent(interval=None) once, then wait at least a short period before calling it again. The second call returns the usage since the first call. For a continuous monitor, you typically store the first result and discard it.

cpu_times() returns a named tuple with user, system, idle, and sometimes iowait and irq fields, depending on the operating system. This is useful for calculating the percentage yourself over a time delta, which gives you more control than cpu_percent() when you are already sampling other metrics.

Monitoring Memory Usage

virtual_memory() returns a named tuple with total, available, percent, used, and free fields. The available field is the most accurate indicator of how much memory can be allocated without swapping, because it includes reclaimable page cache on Linux.

mem = psutil.virtual_memory() print(f"Total: {mem.total / (1024**3):.1f} GiB") print(f"Available: {mem.available / (1024**3):.1f} GiB") print(f"Used: {mem.used / (1024**3):.1f} GiB ({mem.percent}%)")

For swap memory, use swap_memory(). It returns total, used, free, percent, sin, and sout. The sin and sout fields count bytes swapped in and out, which is useful for detecting heavy swapping that can degrade performance.

Collecting Disk Usage and I/O Counters

disk_usage(path) returns a named tuple with total, used, free, and percent for the filesystem containing path. The path must exist; otherwise psutil raises FileNotFoundError.

usage = psutil.disk_usage('/') print(f"Root disk usage: {usage.percent}%")

disk_io_counters() returns a named tuple with read_bytes, write_bytes, read_count, write_count, and other fields. On Linux, this aggregates all disks by default. To get per-disk counters, pass perdisk=True to receive a dictionary keyed by disk name.

io = psutil.disk_io_counters() if io: print(f"Read: {io.read_bytes / (1024**3):.2f} GiB") print(f"Write: {io.write_bytes / (1024**3):.2f} GiB")

Note that disk_io_counters() may return None on some systems or when the OS does not expose the required kernel counters. Always check for None before accessing fields.

Tracking Network I/O and Connections

net_io_counters() returns a named tuple with bytes_sent, bytes_recv, packets_sent, packets_recv, and errin/errout. This gives cumulative totals since boot, so you typically compute deltas between samples to get a rate.

import time def network_rate(interval=1): before = psutil.net_io_counters() time.sleep(interval) after = psutil.net_io_counters() sent_rate = (after.bytes_sent - before.bytes_sent) / interval recv_rate = (after.bytes_recv - before.bytes_recv) / interval return sent_rate, recv_rate

For active connections, net_connections(kind='inet') returns a list of connection objects with fd, family, type, laddr, raddr, status, and pid. This requires elevated privileges on some platforms to see processes owned by other users. On Linux, you may need to run the script as root to get the full list; otherwise, a PermissionError is raised.

Handling Errors and Cross-Platform Differences

psutil abstracts most OS differences, but not all functions behave identically on every platform. For example, cpu_times() includes iowait on Linux but not on macOS or Windows. net_connections() requires different permission levels across systems. Always wrap calls that may fail in try/except and handle PermissionError, FileNotFoundError, and psutil.Error subclasses.

try: conns = psutil.net_connections(kind='inet') except (psutil.AccessDenied, psutil.NoSuchProcess) as e: print(f"Cannot list connections: {e}")

Also be aware that some functions return None when the underlying OS does not provide data, such as disk_io_counters() on certain virtualized systems. Check for None before using the result.

Performance Considerations When Polling Metrics

Calling cpu_percent(interval=1) blocks the current thread for one second. In a monitoring loop that also reads memory and disk, this serializes the sampling and can make the loop slower than necessary. A better approach is to use cpu_percent(interval=None) and sample at your own pace, then compute the delta yourself using cpu_times().

import time def cpu_delta(): t1 = psutil.cpu_times() time.sleep(0.5) t2 = psutil.cpu_times() user_delta = t2.user - t1.user system_delta = t2.system - t1.system total_delta = (t2.user + t2.system + t2.idle) - (t1.user + t1.system + t1.idle) return (user_delta + system_delta) / total_delta * 100

This avoids blocking and lets you read all metrics in a single pass. For network and disk I/O, always compute deltas from cumulative counters rather than relying on a single reading, because the raw values are monotonic since boot.

When building a long-running monitor, be mindful of the overhead of calling net_connections() frequently. Parsing the full connection table can be expensive on systems with many sockets. If you only need total bytes, stick with net_io_counters() and avoid net_connections() unless you specifically need per-process or per-socket details.

python psutil cpu memory disk and network usage: Practical U | RYUSLOG DEV