2026年11月12日

2026年11月12日

Postfix Performance Tuning: Find Bottlenecks with Queues and Logs

This article is a localized edition of the Japanese original. Read the Japanese original

When a mail server is slow, simply raising numbers often backfires. Postfix is designed to work well with its defaults in most environments, and slowness usually comes from one of these: too few receiving processes, the remote server's acceptance limits, slow DNS, or retries to undeliverable destinations.

This article shows how to first measure where mail is getting stuck and then change only the settings that match that spot. Parameter names and defaults were checked with postconf -d on Postfix 3.10 (the Debian 13 package) and in the official documentation (postconf(5), TUNING_README) as of November 2026.

Step 1: Check the current settings and defaults

# Postfix version
postconf mail_version

# Show only settings changed from the defaults
postconf -n

# Individual values (add -d for the defaults)
postconf default_process_limit default_destination_concurrency_limit
postconf -d default_process_limit default_destination_concurrency_limit

If you pass a parameter name that does not exist, postconf warns with unknown parameter. Before applying settings copied from articles online, use this command to confirm the names are correct.

Step 2: Measure where mail is getting stuck

Check the queue sizes

# List mail in the queue (the last line shows the count and total size)
postqueue -p | tail -n 1

# Counts by queue, destination domain and age
qshape active
qshape deferred
qshape incoming

qshape is a Perl script that comes with Postfix. On Debian and Ubuntu it is in the postfix package; on RHEL-based systems it is in the postfix-perl-scripts package.

Queue with many messages Meaning Common causes
incoming Received but not yet placed in line for delivery Slow disk, the queue manager cannot keep up
active Waiting in line for delivery or being delivered Slow connections to remote hosts, too little concurrency, remote acceptance limits
deferred Failed once and waiting for a retry Mail to nonexistent domains or unresponsive servers, rejections or temporary errors from the remote side

If a particular domain piles up in deferred, that is not a performance problem but a problem with an undeliverable destination. Check the reason in the log.

Read delays= in the log

Each delivery log line records which stage took time.

postfix/smtp[12345]: 4A1B2C3D4E: to=<user@example.net>, relay=mx.example.net[192.0.2.10]:25, delay=3.2, delays=0.05/0.01/2.9/0.24, dsn=2.0.0, status=sent (250 2.0.0 OK)

The parts of delays=a/b/c/d mean the following (per postconf(5)).

Value Meaning What to suspect when it is large
a From message arrival until it enters the delivery line (active queue) Heavy processing on receipt, a congested queue manager
b From entering the delivery line until connection setup begins Too few delivery processes or too little per-destination concurrency
c Connection setup (DNS lookups, TCP connect, EHLO, STARTTLS) Slow DNS, a slow remote server, slow TLS handshakes
d Message transmission A slow link, large messages, a slow receiving server

The log is /var/log/mail.log on Debian and Ubuntu (journalctl -u 'postfix*' on systems without rsyslog) and /var/log/maillog on RHEL-based systems.

# Find deliveries whose connection setup (c) took 5 seconds or more
grep -E 'delays=[0-9.]+/[0-9.]+/([5-9]|[1-9][0-9]+)(\.[0-9]+)?/' /var/log/mail.log | tail

# Records of hitting the process limit
grep 'has reached its process limit' /var/log/mail.log | tail

Step 3: Receiving side (smtpd) settings

Number of processes

The maximum number of smtpd and other processes running at once is set by the maxproc column of each line in master.cf. If the column is -, default_process_limit (default 100) is used.

When the limit is reached, the log shows a warning such as service "smtp" (25) has reached its process limit, and new connections wait for a free process. Raise the limit only when you see this warning and have spare memory.

# Example: set the default limit for all services to 150
postconf -e 'default_process_limit = 150'

Raising the limit also increases memory use. If processes are filled by connections from spam senders, blocking them at the entrance with postscreen works better than raising the limit (master.cf contains a commented-out template).

Per-client limits (anvil)

Parameter Default Description
smtpd_client_connection_count_limit 50 Simultaneous connections one client may open
smtpd_client_connection_rate_limit 0 (disabled) Connection attempts per time unit for one client
smtpd_client_message_rate_limit 0 (disabled) Messages per time unit for one client
smtpd_client_recipient_rate_limit 0 (disabled) Recipients per time unit for one client
anvil_rate_time_unit 60s Time unit for the rate limits above

These limits exist to prevent abuse; they are not settings for speed. The official documentation also warns against using them to regulate legitimate mail traffic. Clients in mynetworks are exempt by default through smtpd_client_event_limit_exceptions.

# Example: allow each external client up to 200 recipients per minute
postconf -e 'smtpd_client_recipient_rate_limit = 200'

Step 4: Sending side (smtp client) settings

Per-destination concurrency and delivery interval

Parameter Default Description
default_destination_concurrency_limit 20 Parallel deliveries to one destination (domain)
default_destination_rate_delay 0s (disabled) Interval between deliveries to the same destination domain. When non-zero, deliveries to that destination go one at a time with a gap between them
default_destination_recipient_limit 50 Recipients sent together in one delivery

Large mailbox providers slow down acceptance by returning temporary errors (4xx) when a lot of mail arrives in a short time. In that case, more concurrency only causes more rejections. For the destinations that reject you, lower concurrency and add a delay.

To use different settings per destination, clone the delivery transport.

(1) Add a transport to master.cf. Duplicate the existing smtp unix ... smtp line and rename it to slow. Keep the column values the same as the original line.

# /etc/postfix/master.cf (add below the existing smtp unix line; match the original columns)
slow      unix  -       -       y       -       -       smtp

(2) Choose the destinations sent via slow. Write the destination domain and the transport name.

# /etc/postfix/transport
example.jp    slow:

(3) Add settings that apply only to slow and reload.

postconf -e 'transport_maps = hash:/etc/postfix/transport'
postconf -e 'slow_destination_concurrency_limit = 2'
postconf -e 'slow_destination_rate_delay = 1s'
postconf -e 'slow_destination_concurrency_failed_cohort_limit = 10'
postmap /etc/postfix/transport
postfix check && postfix reload

Replacing the default_ prefix of a parameter name with the master.cf transport name (here slow) makes it apply to that transport only. When you set a delivery interval, follow the official documentation and set _destination_concurrency_failed_cohort_limit to 10 or more. If it stays small, a short run of temporary failures can make Postfix treat the destination as dead and stop delivering to it.

If you already use transport_maps, add the line to your existing file.

Retry intervals and queue lifetime

Many retries to undeliverable destinations use up delivery processes and connections by themselves.

Parameter Default Description
queue_run_delay 300s How often the deferred queue is scanned
minimal_backoff_time 300s Minimum wait before a retry
maximal_backoff_time 4000s Maximum wait before a retry
maximal_queue_lifetime 5d How long before giving up and returning the message to the sender
bounce_queue_lifetime 5d How long before giving up on the bounce message itself

If only your own systems send through the server (form notifications and the like) and there is no need to wait five days, a shorter lifetime keeps the deferred queue from growing.

postconf -e 'maximal_queue_lifetime = 2d'
postconf -e 'bounce_queue_lifetime = 1d'

Shorter retry intervals add load to both the remote side and your own server. The official documentation (TUNING_README) also warns that more frequent retries can slow down delivery overall.

Step 5: TLS and DNS

TLS session cache

Establishing TLS takes time. A session cache makes reconnections to the same host faster. The default main.cf on Debian and Ubuntu already includes the setting for the sending side.

postconf smtp_tls_session_cache_database
# Set it if empty
postconf -e 'smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache'

Check the available database types (btree, lmdb, and so on) with postconf -m.

DNS

If the c value in delays= is large, or the receiving log records clients as unknown, DNS is slow or failing. The official documentation (TUNING_README) recommends running a caching DNS server (a local resolver) on the server. Query the DNS server listed in /etc/resolv.conf with dig and check the response time.

dig example.net MX | grep 'Query time'

Step 6: Apply and verify

# Check the configuration files for errors
postfix check

# Reload the configuration (master.cf changes are applied too)
postfix reload

# Confirm the settings you changed
postconf -n

Running postfix reload resets the timer state of _destination_rate_delay. After a change, look at qshape and the delays= values from Step 2 again to see whether the stage that was stuck has improved. Revert settings that do not help.

Summary

  • First identify where mail is stuck (receiving, waiting for delivery, connection setup, transmission) with qshape and delays=a/b/c/d in the log.
  • Raise the process limit only when the "process limit" warning appears. If spam fills the processes, use postscreen.
  • The smtpd_client_* limits are for abuse prevention. The correct name of the recipient limit is smtpd_client_recipient_rate_limit.
  • For destinations that reject you, split them into a separate transport, lower concurrency and add a delivery interval.
  • When the deferred queue is large, deal with retry settings, queue lifetime and undeliverable destinations first.

Related articles

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲