Postfix logs “too many open files” or “unable to fork.” Inbound SMTP connections fail. Queue files cannot be opened or written. The master process is still running, ports are still bound, and the disk has free space and inodes.

This is file descriptor exhaustion: a Postfix process has reached its per-process soft limit on open file descriptors. Each open socket, queue file, and pipe handle counts toward the limit. On most Linux distributions the default soft limit is 1024, which is inadequate for a production MTA. Under enough concurrent load, a process hits the ceiling and Postfix fails.

Queue write errors look like disk problems. Connection refusals look like firewall issues. The master process appears healthy, so liveness checks pass while mail silently stops flowing.

What this means

Postfix is a multi-process MTA. The master daemon spawns specialized child processes (smtpd, smtp, qmgr, cleanup, bounce, local, virtual, and others) on demand. Each process opens file descriptors: network sockets, handles for queue files in /var/spool/postfix/, pipes for IPC, and handles for maps and lookup tables.

The kernel enforces a per-process limit on open file descriptors via the soft and hard ulimits. When the descriptor count for a single process reaches its soft limit, further open() calls fail with EMFILE and the process logs “too many open files.”

flowchart TD
    A[Connections and queue files consume fds] --> B[fd count approaches ulimit]
    B --> C{At limit?}
    C -->|Yes| D[Daemons cannot open queue files]
    C -->|Yes| E[Master cannot spawn new processes]
    D --> F["too many open files in logs"]
    E --> G["unable to fork in logs"]
    F --> H[New connections refused]
    G --> H
    H --> I[Queue processing stalls]
    I --> J[Looks like disk or network problem]

This is a cliff-edge failure, not gradual degradation. Below the limit, everything works. At the limit, new connections are refused, queue files cannot be opened, and the queue manager cannot spawn delivery agents.

The 1024 default. The kernel default soft limit for open files is 1024 on most Linux distributions. A single smtpd process handling a burst of inbound connections can exhaust its own allocation. Production Postfix deployments typically need 65536 or more file descriptors per process.

Why this looks like other problems:

  • Queue file write errors look similar to disk-full or inode-exhaustion failures.
  • Connection refusals on port 25 look identical to firewall blocks.
  • Delivery stalls look like DNS failures or destination unreachability.
  • The master process is alive and ports are listening, so health checks pass.

The distinguishing signal is the error string in the logs. “too many open files” is unambiguous. “unable to fork” may also appear but has additional causes (process limits, memory exhaustion).

Common causes

CauseWhat it looks likeFirst thing to check
Default ulimit (1024) too low for volumeWorks at low load, fails under burstcat /proc/$(cat /var/spool/postfix/pid/master.pid)/limits | grep "open files"
systemd ignoring limits.confEdited limits.conf and restarted; limit unchangedsystemctl show postfix | grep LimitNOFILE
Queue growth consuming fdsLarge deferred queue; each in-flight message holds a descriptorfind /var/spool/postfix/deferred -type f | wc -l
Process limit raised without raising fd limitmaster.cf maxproc increased; more processes competing for fdspostconf -M | awk '{print $1, $2, $7}'

Quick checks

These commands are read-only and safe for production.

# Master process fd limits (soft and hard)
cat /proc/$(cat /var/spool/postfix/pid/master.pid)/limits | grep "open files"

# Per-process fd counts, sorted by usage (highest first)
for pid in $(pgrep -f postfix); do
  echo "$(ls /proc/$pid/fd 2>/dev/null | wc -l) $pid $(cat /proc/$pid/comm 2>/dev/null)"
done | sort -rn | head -20

# Search logs for fd exhaustion errors
grep -E 'too many open files|unable to fork' /var/log/mail.log | tail -20

# Current smtpd process count
pgrep -c smtpd

# View master.cf service limits (service, type, maxproc)
postconf -M | awk '{print $1, $2, $7}'

# systemd's effective LimitNOFILE
systemctl show postfix | grep LimitNOFILE

# Current shell ulimit for comparison
ulimit -n

The per-process sorted output is the most useful diagnostic. A single process approaching its soft limit is the problem, not the aggregate count across all processes.

How to diagnose it

  1. Confirm the error in logs. Search for “too many open files” in the mail log. Note which daemon logged it (smtpd, qmgr, smtp, local). If you see “unable to fork” without “too many open files,” also check RLIMIT_NPROC and memory.

  2. Check the effective fd limit. Read /proc/<master_pid>/limits for the soft and hard limits actually applied to the master process. If the soft limit is 1024 under moderate load, the limit is the problem.

  3. Find the fd consumer. Use the per-process fd count from Quick checks. Look for the process with the highest count. Determine whether pressure comes from inbound connections (high smtpd count), outbound delivery concurrency (high smtp count), queue depth, or process count (maxproc too high relative to the fd limit).

  4. Verify systemd applied the limit. On systemd-managed distributions (RHEL 7+, Ubuntu 15.04+, Debian 8+), PAM limits in /etc/security/limits.conf are not read for services. Run systemctl show postfix | grep LimitNOFILE to see the effective value. This is the most common operator mistake: editing limits.conf and seeing no effect.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Open fd count per processDirect measure of resource consumptionApproaching 80% of soft limit
Soft fd limit (ulimit -n)The ceiling that triggers the cliff-edgeBelow 65536 for production
smtpd process count vs maxprocHigh process count means high fd consumptionSustained above 80% of maxproc
Total queue file countEach in-flight file holds a descriptorGrowing trend correlating with fd pressure
“too many open files” log frequencyConfirms active exhaustionAny occurrence
Connection refusal rateSymptom of exhausted listener poolSpike correlating with fd pressure

Fixes

Raise the fd limit

The core fix is raising the file descriptor limit for Postfix processes.

On systemd-managed systems (modern Linux), create a drop-in override:

systemctl edit postfix.service

Add:

[Service]
LimitNOFILE=65536

Apply and restart:

# Reload systemd to pick up the override
systemctl daemon-reload

# Full restart required - postfix reload does NOT apply new limits
systemctl restart postfix

postfix reload sends a HUP to the master process, which re-reads configuration but does not change ulimits. Only systemctl restart re-executes the master under systemd with the new LimitNOFILE.

On non-systemd systems, edit /etc/security/limits.conf:

postfix soft nofile 65536
postfix hard nofile 65536

Then restart Postfix (service postfix restart or /etc/init.d/postfix restart). On systemd-managed systems, this file is ignored for services launched by systemd.

Verify after restart:

cat /proc/$(cat /var/spool/postfix/pid/master.pid)/limits | grep "open files"

Drain queue-driven fd pressure

If fd exhaustion is driven by a large deferred queue rather than a low limit, raising the limit alone may not be enough. A deferred queue with hundreds of thousands of messages creates sustained fd pressure as the queue manager scans and retries entries.

# Check deferred queue size
find /var/spool/postfix/deferred -type f | wc -l

# Emergency: hold all mail to stop queue processing and free fds
# WARNING: This stops ALL delivery. Use only as emergency relief.
postsuper -h ALL

# After raising the fd limit, requeue in controlled batches
postsuper -r ALL

postsuper -h ALL moves all messages to the hold queue, immediately reducing fd pressure. After raising the limit, release mail in controlled batches. If the queue is too large to requeue at once, filter by domain or age before releasing.

Fix process limit mismatch

If master.cf maxproc values were raised without a corresponding fd limit increase, more processes compete for the same descriptor ceiling. Each additional smtpd or smtp process opens its own set of sockets, pipes, and file handles.

# Review current maxproc settings (field 7 in master.cf format)
postconf -M | awk '{print $1, $2, $7}'

# Check default_process_limit
postconf -h default_process_limit

Ensure the fd limit accounts for worst case: maxproc multiplied by average fds per process. Alternatively, reduce maxproc to a level the current fd limit can sustain while you investigate the root cause.

Lowering maxproc reduces fd pressure but also reduces peak throughput. Use this as a temporary measure while you raise the fd limit to the correct value.

Prevention

  • Set LimitNOFILE=65536 (or higher) on every production Postfix deployment, regardless of current volume.
  • Monitor fd utilization as a percentage of the soft limit. Alert at 80%.
  • Track smtpd and smtp process counts against maxproc. Sustained operation near maxproc means one traffic spike away from fd pressure.
  • Watch queue depth trends. Growing queues consume fds.
  • Document the systemd override pattern in your runbook. The most common mistake is editing limits.conf on a systemd system and seeing no effect.
  • Verify LimitNOFILE after Postfix package updates. Check /proc/<master_pid>/limits after each upgrade.

How Netdata helps

Netdata surfaces signals that shorten time to diagnose fd exhaustion:

  • Per-process file descriptor counts at per-second resolution, showing fd utilization trending toward the limit before the cliff edge.
  • Configurable alerts that fire at 80% of the configured soft limit.
  • Correlation between fd count, process count, and queue depth on a single timeline. When all three trend upward simultaneously, the root cause is immediately visible.
  • smtpd and smtp process utilization monitoring to identify whether fd pressure originates from inbound volume or outbound delivery concurrency.
  • Log-based alerts on error strings like “too many open files” the moment they appear.