A Dovecot server can reject new mail with No space left on device while df -h still reports gigabytes free. Maildir stores each message as a separate file, so delivery also needs a free inode—the filesystem record used to represent that file. Prove block and inode capacity on the mailbox filesystem separately before changing quotas, deleting mail, or retrying the queue.
The fastest safe diagnosis is a four-way split: filesystem blocks, filesystem inodes, Dovecot quota, and access state. Those failures can produce similar delivery symptoms, but their owners and recoveries differ. Preserve the first error, locate the actual mailbox mount, then restore headroom without treating users’ messages or Dovecot index files as disposable cleanup targets.
Begin with the Dovecot and MTA receipts from the same few minutes. ENOSPC or No space left on device points toward filesystem allocation; Quota exceeded names a policy limit; Permission denied and Read-only file system need different recovery. Current Dovecot logging documentation includes doveadm log find for discovering configured log destinations.
USER_ID='mailbox-user'
doveadm log find
journalctl -u dovecot --since '-15 minutes' --no-pager
doveadm user "$USER_ID"
On Postfix hosts, record the deferred reason and queue ID before any retry. Postfix mail queue triage covers sender, recipient, transport, destination and first-deferral evidence in more depth. A queue that still holds the message is protection against loss, not permission to replay everything repeatedly while delivery storage remains unable to create files.
postqueue -p
postqueue -j | head -n 20
Do not run broad postsuper delete or requeue commands as an inode fix. Preserve queued mail and the original error until the mailbox filesystem can accept one controlled delivery again.
Dovecot’s current Maildir format documentation describes the tmp, new, and cur directories and the one-file-per-message layout. A 2 KB message may consume only one small block allocation, but it still needs a directory entry and inode. Millions of small messages can therefore exhaust the file count long before they consume every data block.
Resolve the mailbox path from local configuration instead of assuming /var/mail or /home. Then ask the filesystem both questions:
MAILROOT='/srv/mail'
findmnt -T "$MAILROOT" -o TARGET,SOURCE,FSTYPE,OPTIONS
df -hT "$MAILROOT"
df -i "$MAILROOT"
GNU df -i reports inode totals, used inodes and available inodes; its current manual page explicitly separates inode information from ordinary block usage. Interpret the same mount, not a convenient parent directory. A bind mount, separate /var, container volume, network mount or user-specific storage mapping can make df -i / irrelevant to the failing mailbox.
The four common states are operationally distinct:
| Evidence on the mailbox mount | Likely owner | Correct next question |
|---|---|---|
| blocks full, inodes available | large data, snapshots, logs or deleted-open files | Which bytes can be moved or safely expired? |
| blocks available, inodes full | very high file count or inode layout | Which directory tree owns the file count? |
| both available, Dovecot quota reached | per-user or namespace policy | Is the quota accurate and intended? |
| capacity available, mount read-only or access denied | filesystem health, mount state, UID/GID or MAC policy | Why can the delivery identity not create a file? |
One label can hide another. For example, an ext4 filesystem may have no allocatable inodes even though user quota also appears near its limit. Repair the filesystem allocation failure first, then confirm that policy counters still match the mailbox state.
Start at the failing mailbox mount and remain on that filesystem. GNU du --inodes counts directory entries rather than bytes; a bounded depth makes the first pass cheaper and usually separates domains, users, Maildir folders, container trees and log directories.
MAILROOT='/srv/mail'
du --inodes --one-file-system --max-depth=3 "$MAILROOT" 2>/dev/null | sort -n | tail -n 30
Run the scan during a controlled window on very large trees because directory traversal itself consumes I/O and metadata cache. Narrow the next pass to the largest branch rather than raising depth across the entire mount. When the owner turns out to be container logs or rotated fragments outside Maildir, the separate Docker log rotation workflow is safer than deleting files by age from a mixed mail volume.
Maildir requires extra restraint. Message filenames and their movement between tmp, new, and cur carry delivery and state semantics. Dovecot also keeps indexes and control data whose location can be configured separately. Do not use find ... -delete, remove dovecot.index* in bulk, or erase messages directly from cur merely because those directories dominate the inode count. Index rebuilds may increase I/O and require additional temporary files at the worst possible time.
Deleted-open files often explain disproportionate block usage, but they can also retain inodes. Linux unlink(2) semantics keep a file in existence until its final open file descriptor closes, so the data blocks and inode are not reusable yet. One large deleted-open file is mainly a byte-capacity clue; many such files can also contribute to inode pressure.
lsof +L1
Filter the path and device columns for the mailbox mount. If the retained files materially explain either exhausted counter, restart only the owning service after confirming impact and rollback; the last close can release both blocks and inodes. Empty output does not clear the mailbox stack—it removes only the deleted-open-file hypothesis.
Dovecot quota is a delivery policy, not a measurement of whether the filesystem can allocate another inode. The current Dovecot quota command exposes per-user quota state, while the quota plugin documentation explains that the configured backend and namespaces determine what is counted. Current doveadm mailbox status documentation requires multiple requested fields to remain one quoted argument.
USER_ID='mailbox-user'
doveadm quota get -u "$USER_ID"
doveadm mailbox status -u "$USER_ID" "messages vsize" INBOX
Compare the reported policy with the exact delivery error. A user below quota does not prove filesystem health; a user at quota does not prove the entire mount is full. Recalculate or repair quota counters only when current Dovecot documentation for the installed version and backend supports that action. Blind counter resets can briefly admit more mail without creating filesystem capacity.
Permission and mount state deserve the same evidence. findmnt will show ro when the filesystem is read-only, while doveadm user helps expose the effective home, mail path and identity mapping. Fix the owning layer rather than applying world-writable permissions to the mailbox tree.
When inodes are exhausted, avoid changes that need more metadata than the mount can provide. New archives, temporary copies, index rebuilds and package updates on the same filesystem can all fail halfway through. Reserve the remaining control-plane space for evidence and a deliberate recovery path.
Use these boundaries:
Object storage can provide an off-host immutable copy when it is designed and tested correctly; MinIO Object Lock backup guidance explains why retention policy alone does not prove a restore. For mail, preserve Dovecot metadata, permissions, ownership and a documented restore procedure rather than copying message bodies without their namespace context.
A small, verified cleanup can create emergency working room only when the files are known disposable application artifacts. Examples might include expired temporary files outside active Maildir delivery paths or an unrelated runaway cache on the same mount. Record the exact paths, retention owner and rollback boundary before removal. User messages, queue files and unknown Dovecot control files are not cleanup candidates.
Durable recovery usually means one of three changes:
For ext4, inode density is largely chosen when the filesystem is created; the current mke2fs manual documents bytes-per-inode and inode-size choices and warns that changing the ratio after creation is not generally possible. Expanding the underlying device and filesystem can add block groups and their inodes, as documented by resize2fs, but that option needs real storage runway and does not change the existing bytes-per-inode design. When expansion is unavailable or the density remains wrong, build a new target, migrate, validate and retain rollback instead of recreating the only copy in place.
Generic live rsync of an active Maildir can race with deliveries and flag changes. Dovecot provides doveadm sync and backup for mailbox-aware replication and migration, but exact commands depend on the installed version, user database, namespaces and target driver. Test one mailbox, preserve UID/GID and security metadata, and use a maintenance or final-sync window before changing the production mail path.
Physical storage still matters. Teams choosing a new host should consult dedicated server hardware guidance to distinguish NVMe endurance, RAID behavior, hot-swap access and backup boundaries rather than treating a larger advertised disk as sufficient mailbox capacity.
No space left on device when df -h is below 100%?Yes. Maildir needs a new inode for each message file, so delivery can return ENOSPC when df -i has no available inodes even though block capacity remains.
dovecot.index files a safe inode fix?Not as a generic incident action. Dovecot may rebuild indexes, which needs I/O and new files, while some control or index locations may be configured outside the visible Maildir. Confirm the installed layout and create durable headroom first.
No. Dovecot quota is a per-user or namespace policy enforced by its configured backend. Check doveadm quota get beside df -h, df -i, the mount options and the exact delivery log.
Sometimes. Growing an ext4 filesystem onto additional storage can add block groups and inodes, but the existing bytes-per-inode ratio cannot normally be changed in place. If growth is unavailable or density remains unsuitable, migrate to a newly created filesystem and validate before cutover.
Recovery requires inode and block reserve on the real mailbox mount, one successful controlled delivery, visible arrival in the intended mailbox, stable Dovecot quota state, and a draining MTA queue without new storage errors.
After recovery, record df -hT, df -i, mount options and the largest inode-owning branches again. Then inject one deliberate test message through the same SMTP and LMTP path used by production. Verify its queue ID, Dovecot acceptance, final mailbox arrival and user visibility before allowing a large backlog to retry.
Watch both file count and bytes as the queue drains:
MAILROOT='/srv/mail'
watch -n 10 "df -h '$MAILROOT'; df -i '$MAILROOT'; postqueue -p | tail -n 1"
Stop the release if available inodes collapse, delivery latency rises sharply, the mount becomes read-only, or quota and mailbox counts diverge. A successful SMTP 250 at the edge is insufficient when the message later defers between Postfix and Dovecot.
Alert on both inode percentage and the rate at which available inodes are falling. A fixed 90% warning can be too late for a high-volume service and noisy for a stable small system. Runway must exceed detection, diagnosis, migration and backlog-drain time under the observed peak message rate.
Capacity reviews should include mailbox count, files per mailbox, daily inode growth, block growth, queued-message peak, temporary-file behavior, backup workspace and restore-test requirements. Readers who operate more Linux services on the same host can continue with Linux operations guidance to keep file, process, network and resource evidence separated by owner.
Teams without root, filesystem or Dovecot authority should hand the failure receipt, mailbox path, findmnt, both df views, top inode owners, quota output and queue state to managed hosting operations. The acceptance record is complete only when a real message survives the full delivery path and the filesystem has enough runway for the next intervention window.