How to Implement Rate-Limited Outgoing Email Delivery in Postfix with 120s Delay Between Messages


2 views

When dealing with email sending from local PHP applications through Postfix, sometimes you need strict control over the delivery rate. The requirement here is specific: enforce a minimum 120-second interval between sending any two email messages, while properly queuing excess messages for later delivery.

The attempted configuration using parameters like default_destination_concurrency_limit and default_destination_rate_delay didn't work because these primarily control connection-level behavior rather than individual message timing. These settings are more suited for managing server load than enforcing precise delays between messages.

Here's how to properly implement message-level rate limiting:

# In main.cf:
default_destination_concurrency_limit = 1
default_destination_rate_delay = 120s
anvil_rate_time_unit = 120
anvil_status_update_time = 120

Then create a dedicated transport in master.cf:

# In master.cf:
slowdelivery unix - - n - 1 smtp
  -o smtp_destination_concurrency_limit=1
  -o smtp_destination_rate_delay=120s

Configure PHP to use this transport by adding headers:

// In your PHP mail script:
$headers = array(
    'X-Postfix-Transport' => 'slowdelivery',
    'X-Postfix-Delay' => '120'
);
mail($to, $subject, $message, $headers);

Check the queue and verify the delay is working:

postqueue -p
postcat -q [queue_id]

Monitor the anvil service for rate limiting statistics:

postfix -c /etc/postfix anvil

If messages aren't being delayed as expected:

  • Verify time units are specified with 's' suffix (120s not 120)
  • Check syslog for Postfix warnings: grep postfix /var/log/syslog
  • Ensure your transport name in master.cf matches what's referenced

For more complex scenarios, consider this queue manager configuration:

# In main.cf:
queue_run_delay = 120s
minimal_backoff_time = 120s
maximal_backoff_time = 120s

This forces Postfix to wait 120 seconds between delivery attempts for all messages in the queue.


When dealing with email delivery from local PHP applications through Postfix, there are scenarios where you need to enforce strict timing between outgoing messages. This isn't about bulk sending or overall throughput limitation, but rather ensuring a precise 120-second gap between each message dispatch.

Common solutions like policyd focus on aggregate limits (messages per hour) rather than inter-message delays. The original configuration attempt using:

initial_destination_concurrency = 1
default_destination_concurrency_limit = 1
default_destination_rate_delay = 120
default_destination_recipient_limit = 1
default_process_limit = 1

doesn't work because these parameters control different aspects of Postfix's delivery behavior, not the precise timing between individual messages.

Here's the complete configuration that will enforce the 120-second delay between messages:

# Main rate limiting parameters
default_destination_rate_delay = 120s
default_destination_concurrency_limit = 1
initial_destination_concurrency = 1
smtp_destination_concurrency_limit = 1

# Queue management
minimal_backoff_time = 120s
maximal_backoff_time = 120s
queue_run_delay = 120s

# Important additional settings
transport_rate_delay = 120s
default_transport_rate_delay = 120s

After making these changes:

  1. Run postfix reload to apply changes
  2. Monitor with mailq to see queued messages
  3. Check logs with tail -f /var/log/mail.log

For more granular control, create a transport map:

# /etc/postfix/transport_rate
example.com   120s
another.com   60s

# main.cf addition
transport_rate_maps = hash:/etc/postfix/transport_rate

Then run postmap /etc/postfix/transport_rate and reload Postfix.

If messages still send too quickly:

  • Verify no other MTA (like sendmail) is interfering
  • Check for multiple Postfix instances
  • Confirm PHP isn't using direct SMTP bypassing Postfix
  • Test with sendmail -v test@example.com < /dev/null