SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

What Is an Inode? Find, Count and Check Them

An inode is the record a file name points to. Learn what it holds, read it with ls -i and stat, count inodes with df -i, and see why ext4 fixes the ratio.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, October 22, 2026.

What is an inode?

An inode is the record that holds everything about a file except its name. A directory entry pairs a name with an inode number, and the inode behind that number holds the owner, the permission bits, the timestamps, the size and the location of the data. Every file uses one inode, so a filesystem can run out of inodes while it still has free space.

The inode(7) man page says it in one line: each file has an inode containing metadata about the file. Each file in a filesystem also has a unique inode number. That number is unique only within one filesystem. Two disks can both hold an inode number 12, and those are two unrelated files.

This guide shows you how to read inode numbers with ls -i and stat, how hard links, symlinks and copies relate to inodes, how to find a file by its inode number, and how to count inodes with df -i and du --inodes. It ends with why ext4 fixes the inode ratio at format time and what can still change later, shown on an image file you can create without root.

What an inode holds, and what it does not

An inode holds the facts the kernel needs to open, check and read a file:

  • The owner, as a user ID and a group ID.
  • The mode: the file type plus the permission bits, the same bits that ls -l prints as drwxr-xr-x.
  • Timestamps for the last access, the last change to the content and the last change to the inode itself.
  • The size in bytes and the number of blocks allocated.
  • The link count, which is the number of hard links (names) that point at this inode.
  • The location of the data. In ext4 (the fourth extended filesystem, the Ubuntu default) this is normally an extent tree, a compact map of runs of blocks, instead of a plain list of block numbers.

The inode does not hold the file name. The ext4 kernel documentation describes the directory entry format struct ext4_dir_entry_2 as five fields: the number of the inode the entry points to, the record length, the length of the file name, a file type code, and the file name. So the name lives in the directory, and the directory is just a list of name and number pairs. This one fact explains most of what follows. A rename edits a directory entry and leaves the inode alone. One inode can have two names, because two directory entries can hold the same number.

On disk, every ext4 block group has an inode table for its share of the inodes. The kernel docs call it a linear array of struct ext4_inode. The table does not have to sit inside its own group. With the flex_bg feature, ext4 collects the tables of several groups together, and the kernel docs say the bitmaps and the inode table can be anywhere. Each inode record has a size that is set at format time, and dumpe2fs reports it as Inode size: later in this guide. The tables are written at format time, which matters later in this guide.

See inode numbers with ls -i and stat

Work in a scratch directory so nothing you care about is touched:

mkdir ~/inode-lab
cd ~/inode-lab
echo "hello" > notes.txt
ls -i notes.txt
stat notes.txt

ls -i prints the index number of each file, which is the inode number, in front of the name. stat prints the full record. Look for the Inode: field, the Links: field, the Access: line with the octal and symbolic permission bits, the Uid: and Gid: owners, and the Access, Modify and Change timestamps. The File: line at the top only repeats the path you typed. It does not come from the inode.

For scripts, ask stat for exactly the fields you want with a format string:

stat -c '%i inode, %h links, owner %U, mode %a, %s bytes, %b blocks: %n' notes.txt

%i is the inode number, %h the number of hard links, %U the owner's user name, %a the permission bits in octal, %s the size in bytes, %b the blocks allocated and %n the file name. You should see one line with a link count of 1, because notes.txt is the only name for that inode so far.

Make one of each and compare them:

ln notes.txt notes-hard.txt
ln -s notes.txt notes-sym.txt
cp notes.txt notes-copy.txt
ls -li

ln makes a hard link by default. It only makes a symbolic link with -s. The first column of ls -li is the inode number, and the third column is the link count.

Read the output this way. notes.txt and notes-hard.txt show the same inode number and a link count of 2. They are two directory entries for one file. Neither one is the original, and a change written through one name shows up through the other. notes-sym.txt shows a different number and starts with l in the mode column, because a symlink is a small file of its own that stores a path. notes-copy.txt also has its own number, because cp wrote a second file with its own data. For the full story on symlinks, read how symbolic links with ln -s work.

Two limits come straight from the inode design. A hard link cannot cross filesystems, because an inode number has no meaning on another filesystem. And ext4 normally does not allow one inode to have more than 65,000 hard links.

Find every name for one inode with find -inum

When you know an inode number and want its names, find -inum matches files by inode number:

find ~/inode-lab -xdev -inum "$(stat -c %i ~/inode-lab/notes.txt)"

You should see both notes.txt and notes-hard.txt. Keep -xdev on every inode search. It stops find from going into other mounted filesystems, which matters because the same number on another filesystem is a different file. The find man page notes that -samefile is usually easier, since it takes a path and looks up the inode for you:

sudo find / -xdev -samefile /home/ubuntu/inode-lab/notes.txt 2>/dev/null

To list every file that has more than one name, match on the link count with -links:

find ~/inode-lab -xdev -type f -links +1

That prints the two hard-linked names and skips the copy.

Does mv change the inode number?

Not within one filesystem. Check it:

mkdir sub
stat -c '%i %n' notes-copy.txt
mv notes-copy.txt sub/
stat -c '%i %n' sub/notes-copy.txt

The two numbers match. The mv man page describes the job as renaming, and inside one filesystem the rename(2) system call does the work. It removes one directory entry and writes another that holds the same inode number. The data is not read or written, which is why moving a large file inside one disk is instant.

Across filesystems the rename cannot happen. rename(2) fails with EXDEV when the two paths are not on the same mounted filesystem, even when one filesystem is mounted twice. mv then copies the data to the other filesystem and removes the original. The file on the other side is a new file with a new inode on that filesystem.

Now delete one name and watch the link count:

rm notes.txt
cat notes-hard.txt
stat -c '%i %h %n' notes-hard.txt
cat notes-sym.txt

notes-hard.txt still prints hello, and its link count is back to 1. The data was never tied to the name you removed. The symlink is now broken, and cat reports cat: notes-sym.txt: No such file or directory, because the path it stores no longer exists.

How many inodes do you have? Use df -i and du --inodes

df -i lists inode information instead of block usage:

df -i /

The columns are Inodes (the total), IUsed, IFree and IUse%. Read IUse% the same way you read the percent column of df -h. A filesystem at 100% inode use cannot create a new file, an empty file included, whatever df -h says about free space.

To see where the inodes went, du --inodes counts inodes instead of blocks. This lists the five busiest top-level directories on the root filesystem:

sudo du --inodes -x -d 1 / 2>/dev/null | sort -n | tail -n 5

Run it again one level down inside the winner. A cache directory with millions of tiny files usually shows up within two or three passes. When the symptom is No space left on device while df -h shows free space, the full incident and fix is in why df and du disagree on a full disk. This page stays with the concept and the lookup commands.

One caution for containers. In a Docker container that uses the overlay2 driver, the root filesystem is a merged view of the image's read-only layers and the container's writable layer, and that storage lives on the host. So df -i inside the container describes the overlay mount, not a disk the container owns. Do not plan around those numbers. Run df -i on the host to see the filesystem that actually holds the files.

Why ext4 fixes the inode count at mkfs time

mke2fs creates one inode for every bytes-per-inode bytes of space, and its man page says this ratio cannot be changed after the filesystem is created. You can see this without a spare disk and without mounting anything, because mke2fs can build a filesystem inside a regular file. e2fsprogs provides mke2fs and dumpe2fs and is installed on standard Ubuntu images. If yours lacks it, install it:

sudo apt update && sudo apt install -y e2fsprogs

Create a 1 GiB image file, format it as ext4 and read the superblock back:

cd ~/inode-lab
truncate -s 1G disk.img
mke2fs -t ext4 -q -F disk.img
dumpe2fs -h disk.img 2>/dev/null | grep '^Inode'

-F tells mke2fs to go ahead even though the target is a file, not a block device. dumpe2fs -h prints only the superblock, and the grep keeps the lines for Inode count:, Inodes per group: and Inode size:. If the shell says mke2fs: command not found, either the program is not installed or it sits in a directory your PATH does not search. Check with ls -l /sbin/mke2fs. If the file is there, call it as /sbin/mke2fs and /sbin/dumpe2fs. If it is not, install e2fsprogs with the command above.

Now format a second image with a larger ratio and compare the counts:

truncate -s 1G big-files.img
mke2fs -t ext4 -q -F -i 65536 big-files.img
dumpe2fs -h big-files.img 2>/dev/null | grep '^Inode count'

The second image reports about a quarter of the inodes of the first, because each inode now stands for four times as much space. That is the whole trade. Fewer inodes leave more room for data and suit large files. More inodes suit millions of small files.

The default ratio comes from /etc/mke2fs.conf, and -T picks a usage type from that file. The upstream e2fsprogs copy sets the default ratio to 16,384 bytes, which works out to 65,536 inodes per GiB (gibibyte). Distributions ship their own copy, so treat these as upstream values and trust the dumpe2fs number for your system.

ChartUpstream mke2fs.conf.in inode ratios by usage type
The data behind this chart
[
  {
    "label": "default",
    "bytes_per_inode": "16,384",
    "inodes_per_gib": "65,536"
  },
  {
    "label": "small",
    "bytes_per_inode": "4,096",
    "inodes_per_gib": "262,144"
  },
  {
    "label": "floppy",
    "bytes_per_inode": "8,192",
    "inodes_per_gib": "131,072"
  },
  {
    "label": "big",
    "bytes_per_inode": "32,768",
    "inodes_per_gib": "32,768"
  },
  {
    "label": "huge",
    "bytes_per_inode": "65,536",
    "inodes_per_gib": "16,384"
  },
  {
    "label": "largefile",
    "bytes_per_inode": "1,048,576",
    "inodes_per_gib": "1,024"
  }
]

The spread is wide. The small type gives 262,144 inodes per GiB, while largefile gives only 1,024. Pick the type when you format a new volume, for example when you add a block storage volume to a VPS that will hold a mail spool or a package cache.

Two more mke2fs options set the shape at format time. -N sets the number of inodes directly instead of computing it from the ratio. -I sets the inode size, a power of 2 of at least 128. The mke2fs man page says the inode size cannot be changed after creation, but tune2fs can change it later: the kernel ext4 documentation shows a filesystem made with 128-byte inodes being converted to 256-byte inodes with tune2fs -I 256 /dev/hda1. The tune2fs man page warns that this rewrites the inode table, needs an e2fsck check first, and can corrupt the filesystem and lose data if it is interrupted, so back up before you try it. A new inode size does not change how many inodes there are. Resizing the filesystem is the one way the count moves: a resize recomputes the inode count to keep the same ratio, so a bigger filesystem gets more inodes but the ratio itself stays fixed. The inode tables are reserved up front, which is also part of why a new disk shows less free space than its label even before you write a file. The share of space they take is the inode size divided by the bytes-per-inode ratio, so divide the Inode size: value that dumpe2fs printed by the ratio to work it out for your image.

Remove the images when you are done:

rm ~/inode-lab/disk.img ~/inode-lab/big-files.img

How XFS handles inodes differently

XFS does not write a fixed inode table at format time. Red Hat's knowledge base describes XFS as allocating inodes as needed while files are created, and notes that the total shown by df -i can change between runs. What XFS does fix is a ceiling: maxpct, the maximum percentage of the filesystem's space that may be used for inodes.

The mkfs.xfs man page sets the default maxpct to 25% for filesystems under 1TB, 5% for filesystems under 50TB and 1% above that. You set it at format time with -i maxpct=nn. Unlike the ext4 ratio, you can change it later on a mounted filesystem with xfs_growfs -m, which takes the new percentage. So an XFS volume full of tiny files can usually be fixed by raising the ceiling, while an ext4 volume with the same problem needs a reformat or a resize.

FAQ

Does an inode store the file name?

No. The name lives in the directory entry, which holds the file name and the number of the inode it points to. That is why one inode can have several names (hard links), and why renaming a file inside one filesystem does not change its inode number or touch its data.

How do I check how many inodes are free on Linux?

Run df -i. The IFree column shows free inodes and IUse% shows the percentage used for each mounted filesystem. To find which directories use the most inodes, run sudo du --inodes -x -d 1 / | sort -n and repeat one level down inside the largest result.

Can I add more inodes to an existing ext4 filesystem?

Yes, by growing it. A resize recomputes the inode count to keep the same bytes-per-inode ratio, so a bigger filesystem gets more inodes without a reformat. What you cannot change is the ratio itself, which mke2fs sets at format time. tune2fs -I can enlarge the inode size, for example from 128 to 256 bytes, but that changes the size of each inode, not how many there are. To get more inodes without growing the disk, back up, reformat with a smaller -i value or a larger -N, and restore.

A hard link does. ls -li shows the same inode number for both names and a link count of 2. A symlink does not. It shows its own number, because it is a separate small file that stores a path, and it breaks if that path is removed. A cp copy also gets its own inode.