Reason 1 – open file deletion

When you delete a file that is being held “open” by a process what actually happens is that the file’s name is deleted but not its inode (and its data). The command df sees what is happening at filesystem level whereas du sees what is happening at file/directory level. If the filename is gone du doesn’t see it in the directory any more.
However since the inode is still in use df sees that the filesystem is still using the space.

You can try to run the command lsof using as parameter the filesystem you are checking and look for any reg file that has a large size but no name. This will show you the process that has it “open”. If you stop then restart the process it will clear the inode.

A good way to look for deleted file in a file system and still

lsof /tmp |egrep"^COMMAND|deleted"

This will list all the files that have been deleted while they were still open.

If this fail that you can do a reboot which will stop all processes and should definitely clear any such open inodes.

By the way doing a “mv” of a file doesn’t help because it keeps the same inode when moved within the filesystem. A “mv” to another filesystem would result in the same situation as the deletion did because such a “mv” actually copies then deletes as the inode is unique to the filesystem it is on.

The most common cause of this is deleting a log file to clear up space. Many log files are held “open” by their applications so deletion should only be done after the application is shutdown. You might want to look at logrotate (man logrotate) to setup automatic aging of logfiles if you have one that is consistently filling up your filesystem.

Reason 2 – files under a mount point

If you mount a directory (say a nfs) onto a filesystem that already had a file or directories under it, you lose the ability to see those files, but they’re still consuming space on the underlying disk.

Small example:

On your linux box you have /usr that uses 4GB
After some time you create a new filesystem and mount it on the mount point /usr/local without “cleaning” the local directory.
So now you have an “underlying” /usr/local location that you cannot access anymore (but that still uses space on the /usr filesystem) and a new filesystem mounted on /use/local, in this case a df -h /usr and a du -hs of the /usr filesystem (without the local directory) will give different results.