Calculate inode usage

Introduction

This guide explains how to calculate inode usage on a Linux server, using a script that breaks down inode consumption directory by directory — the exact technique needed to troubleshoot “Disk quota exceeded” errors on cPanel and other hosting environments, which are very often actually caused by running out of inodes, not disk space.

The original version of this script that circulates in a lot of quick-reference posts has a few real bugs baked in — smart quotes from copy-pasting out of a word processor, a subtle filtering issue that can silently exclude legitimate directories, and missing variable quoting that breaks on directory names containing spaces. This guide walks through a corrected, safe version, along with the background needed to actually understand what inodes are and why they matter.


Implementation

I. Prerequisites

Before you calculate inode usage, make sure you have:

  • SSH or terminal access to a Linux server
  • Basic familiarity with the Bash shell
  • Sufficient permissions to read the directories you want to analyze (root access if inspecting system-wide or another user’s directories)

II. What Are Inodes, and Why Do They Matter?

Every file and directory on a Linux filesystem is represented internally by an inode — a data structure that stores metadata about the file (permissions, ownership, timestamps, and pointers to the actual data blocks on disk) but notably not the filename itself, which is stored separately in the containing directory’s entry.

Here’s the detail that trips a lot of people up: every filesystem has a fixed number of inodes, set when the filesystem is created, completely independent of how much disk space it has. This means you can run out of inodes — and be unable to create a single new file — even while gigabytes of disk space remain free, simply because you’ve created too many individual files (a huge number of tiny cache files, session files, or email messages are the classic culprits).

This is exactly why “Disk quota exceeded” errors on shared hosting or cPanel servers are frequently an inode problem, not a storage-space problem — and why being able to calculate inode usage per directory, not just check overall free space, is essential for actually diagnosing the issue.

III. How Inodes Relate to Files and Disk Blocks

Before running any commands, it helps to see the relationship visually:

  • Each file or directory you create consumes exactly one inode, regardless of the file’s actual size
  • The inode stores metadata and pointers to the file’s actual data blocks on disk
  • A filesystem’s inode table has a fixed capacity, set at filesystem creation — once every inode is used, no new files can be created, even with free disk space remaining

Understanding this relationship is what makes the difference between two very different troubleshooting paths: “I’m out of disk space” (a data-block problem) versus “I’m out of inodes” (a file-count problem) — and the fix for each is completely different.

IV. Check Overall Filesystem Inode Usage First

Before diving into a specific directory, get the big picture. This command shows inode usage across all mounted filesystems:

df -i

Look at the IUse% column. If it’s at or near 100% for the filesystem in question, you’ve confirmed an inode exhaustion problem — which is exactly what the directory-level script in the next steps helps you pinpoint.

V. Understand What the Directory-Level Script Does

The goal of the script is straightforward: for each subdirectory in the current location, count how many inodes (effectively, how many files and directories) it contains, and print a sorted breakdown — making it easy to spot which specific directory is responsible for the bulk of inode usage.

A version of this script that circulates in various troubleshooting posts online has a few real problems, worth calling out directly since they’d cause it to fail or behave incorrectly if copy-pasted as-is:

  • Smart/curly quotes (" " instead of straight ") — a common artifact of copying script text out of a word processor or web page, which breaks Bash syntax entirely, since the shell doesn’t recognize curly quote characters as string delimiters
  • An unescaped . in the grep filter — the filter is meant to exclude the current directory entry (.) from the list, but . is a regex special character matching any single character when left unescaped. This means the original script would also incorrectly exclude any legitimate single-character directory name (like a directory literally named a or 1)
  • Missing quotes around variables — without quotes around $i in commands like find $i, directory names containing spaces would be split into multiple arguments and processed incorrectly

VI. The Corrected Script

Here’s a corrected, safer version that avoids all three issues above:

#!/bin/bash

printf "===\nInode usage: %s\n===\n\n" "$(pwd)"

for dir in $(find . -maxdepth 1 -mindepth 1 -type d | sed 's|^\./||' | sort); do
    tot=$(find "$dir" | wc -l)
    printf "%s \t\t -> %s\n" "$tot" "$dir"
done

printf "Total:\t \t %s\n" "$(find "$(pwd)" | wc -l)"

What changed, and why:

FixExplanation
Straight quotes throughoutEnsures valid Bash syntax regardless of how the script is copied or pasted
-mindepth 1 added to findExcludes the current directory (.) directly at the find level, removing the need for the fragile grep -xv . filter entirely
`sed ‘s^./
"$dir" quoted throughoutPrevents directory names containing spaces from being split into multiple words and misinterpreted
$(...) instead of backticksModern, more readable command substitution syntax that also nests more reliably than backticks

VII. Run the Script

Save the script to a file, for example inode-usage.sh, make it executable, and run it from the directory you want to analyze:

chmod +x inode-usage.sh
cd /home/username/public_html
/path/to/inode-usage.sh

VIII. Understanding the Output

The script prints one line per subdirectory, showing the total inode count (files + subdirectories + the directory itself) alongside the directory name, followed by a grand total for the current directory:

===
Inode usage: /home/username/public_html
===

     3421 		 -> cache
      842 		 -> logs
   128954 		 -> mail
      210 		 -> public
Total:	 	 133427

In this example, the mail directory is overwhelmingly responsible for the bulk of inode usage — a common real-world pattern, since accumulated email messages (each one its own small file) are one of the most frequent causes of inode exhaustion on hosting accounts.

IX. Alternative One-Liner for a Quick Check

If you just need a fast top-level check without saving a script, this one-liner accomplishes something similar for immediate subdirectories, sorted by count:

for dir in */; do echo "$(find "$dir" | wc -l) $dir"; done | sort -rn

This lists each immediate subdirectory’s inode count, sorted from highest to lowest, making it quick to spot the biggest offender without needing to save a separate script file first.

X. Common Pitfalls When Counting Inodes

  • Running the script from the wrong directory. The script only analyzes the current directory’s immediate subdirectories — running it from your home directory when the actual problem is deep inside public_html/cache will show public_html as one large number without pinpointing the real culprit. Drill down progressively, re-running the script inside the largest offending directory each time.
  • Forgetting that hidden directories count too. The script does include dotfiles/dot-directories in its subdirectory scan (since find -mindepth 1 picks them up), but it’s easy to overlook a hidden directory like .cache when scanning output visually — don’t assume the biggest number is always in a “normal-looking” directory name.
  • Not distinguishing files from directories in totals. The script’s counts include both files and directories together, which is appropriate for inode counting (since directories consume inodes too) but can be initially confusing if you expected a pure file count.

XI. Fixing Inode Exhaustion Once You’ve Found the Cause

Once you’ve identified the directory consuming the most inodes, a few common fixes apply depending on what’s found:

  • Accumulated email in a mail directory: Set up mail retention policies, archive or delete old messages, or configure your mail client to not leave copies on the server indefinitely
  • Cache directories: Most application caches (WordPress plugins, framework temp directories, etc.) can be safely cleared — check the specific application’s documentation for the recommended cache-clearing method rather than deleting files blindly
  • Session files: PHP and other application session directories can accumulate stale session files over time if garbage collection isn’t configured correctly — this is worth fixing at the configuration level, not just cleaning up after the fact
  • Log files: Old, unrotated logs are a frequent culprit — configure logrotate (standard on most Linux distributions) to automatically compress and eventually delete old log files instead of letting them accumulate indefinitely

XII. Preventing Future Inode Exhaustion

  • Set up log rotation for any application or service generating log files, so they don’t accumulate unbounded
  • Monitor inode usage proactively with df -i, ideally as part of a regular server monitoring routine rather than only checking after a quota error appears
  • Review application-level cache and temp-file cleanup settings, since many frameworks and CMS platforms generate large numbers of small temporary files by design and need periodic cleanup configured
  • Consider your hosting plan’s inode limits when choosing shared hosting, since inode caps (common on cPanel-based shared hosting) are a real constraint independent of disk space, and heavy file-generating workloads can hit them well before storage space becomes an issue

XIII. Conclusion

Calculating inode usage directory by directory is the key diagnostic step for tracking down “Disk quota exceeded” errors that turn out to have nothing to do with actual disk space. The corrected script above avoids the quoting, filtering, and word-splitting bugs present in versions that circulate online, and pairs with df -i for confirming the filesystem-level picture before drilling into specific directories. Once you’ve found the offending directory, the fix is usually straightforward — clear accumulated cache, mail, session, or log files — but proactive monitoring and log rotation are what actually prevent the problem from recurring.

For more background on how Linux filesystems manage inodes, see the Linux Filesystem Hierarchy documentation.


Frequently Asked Questions

How do I know if my problem is disk space or inode exhaustion? Run df -h to check disk space and df -i to check inode usage side by side. If df -i shows the IUse% column near 100% while df -h shows plenty of free space, you’re dealing with inode exhaustion specifically, not a storage capacity issue.

Can I increase the number of inodes on an existing filesystem? Generally, no — the inode count is fixed at filesystem creation time for most common filesystems like ext4. Increasing it typically requires reformatting the filesystem with a different inode ratio, which means backing up data first. This is why proactive monitoring and cleanup are more practical than trying to expand capacity after the fact.

Why does my hosting provider count inodes at all? On shared hosting environments like cPanel, inode limits are one way providers manage overall filesystem health and prevent any single account from consuming a disproportionate share of the shared filesystem’s fixed inode capacity, which could otherwise degrade the experience for other accounts on the same server.

Does deleting large files help with inode exhaustion? Not necessarily — a single large file still only consumes one inode, the same as a tiny file. Inode exhaustion is caused by having too many individual files, regardless of their size, so the fix is reducing file count, not file size.

Is there a way to automate this check on a recurring schedule? Yes — the corrected script from Step VI can be wrapped in a cron job that runs periodically (daily or weekly) and emails the output, or logs it to a file for review, giving you an early warning before inode usage reaches a critical threshold rather than discovering the problem only after a quota error appears.


If you have any questions about this setup or run into an issue not covered here, feel free to reach out to us at Pheonix Solutions — we’re happy to help.

Related Articles:

How the Linux Kernel Handles Network Connections Using DNS and Routing

How DNS Name Resolution Works in Linux

admin

Writes about Containers & Kubernetes at Pheonix Solutions.

Leave a Reply

Scroll to Top