“Connection refused” on port 25 or 587 means no process on the host is completing the TCP handshake on that port. This is distinct from a timeout, where something accepts the SYN but never responds. “Refused” means either no process is listening, or the kernel is actively rejecting the connection.

The most common real-world causes: the master process is down, Postfix is bound to loopback only, the smtpd process pool is exhausted, or a firewall is intercepting traffic before Postfix sees it. Less common but worth checking: inet_protocols mismatch on IPv6-disabled hosts, port conflicts with other MTAs, and postscreen handoff failures.

What this means

For Postfix, “connection refused” has two primary failure shapes:

  1. No socket at all: The master process is not running, or it failed to bind the configured port due to an interface mismatch, IPv6 issue, or port conflict.

  2. Socket present but not serving: Postfix or postscreen is listening, but all smtpd workers are consumed by slow clients or hung on DNS or milter lookups. New connections are refused because master cannot spawn another smtpd within the maxproc limit.

A third pattern that looks like “Postfix not listening” is a firewall or SYN-cookie layer intercepting traffic. The socket is present and Postfix is healthy, but external clients cannot reach it. Always test both locally and remotely.

flowchart TD
    A["Connection refused
on port 25 or 587"] --> B{"Socket present?"} B -->|"No"| C{"Master process running?"} B -->|"Yes"| D{"220 greeting received?"} C -->|"No"| E["Master down or
failed to bind"] C -->|"Yes"| F["Interface or protocol
mismatch"] D -->|"No"| G["smtpd exhausted
or hung"] D -->|"Yes"| H{"Works locally,
fails remotely?"} H -->|"Yes"| I["Firewall or
ISP block"] H -->|"No"| J["Postscreen handoff
or greeting mismatch"]

Common causes

CauseWhat it looks likeFirst thing to check
Master process not runningNo socket on port 25 or 587; postfix status reports not runningcat /var/spool/postfix/pid/master.pid and verify PID
inet_interfaces bound to loopbackSocket on 127.0.0.1:25 only; external connections refusedpostconf -h inet_interfaces
inet_protocols mismatch (IPv6 disabled)Postfix logs “cannot assign requested address”; no socket boundpostconf -h inet_protocols vs host IPv6 state
master.cf explicit IP bindingPostfix bound to 127.0.0.1 regardless of inet_interfaces = allpostconf -M smtp/inet
smtpd maxproc exhaustionSocket present but no 220 greeting; “to limit” in logsps aux | grep smtpd | grep -v grep | wc -l vs maxproc
Postscreen handoff failurePostscreen answers on 25 but smtpd pass failsTest both postscreen and direct smtpd paths
Firewall or SYN-cookie interceptionLocal connection works; remote fails or times outExternal nc -zv host 25 vs local
Port conflict (sendmail, Dovecot, Exim)Another process holds port 25 or 587ss -tlnp | grep -E ':(25|587) '

Quick checks

# Check master process liveness via PID file
cat /var/spool/postfix/pid/master.pid && ps -p $(cat /var/spool/postfix/pid/master.pid) -o pid,comm,start_time

# Check if Postfix thinks it is running
postfix status

# Validate configuration syntax before attempting restart
postfix check 2>&1

# Check listening sockets on SMTP ports
ss -tlnp | grep -E ':25 |:587 '

# Test the SMTP greeting locally (should return 220 with hostname)
echo QUIT | nc -w 5 localhost 25 | head -1

# Test submission port locally
echo QUIT | nc -w 5 localhost 587 | head -1

# Check inet_interfaces and inet_protocols configuration
postconf -h inet_interfaces
postconf -h inet_protocols

# Check master.cf service definitions
postconf -M smtp/inet
postconf -M submission/inet

# Count active smtpd processes (compare against maxproc)
ps aux | grep smtpd | grep -v grep | wc -l

# Look for maxproc exhaustion warnings
grep 'to limit' /var/log/mail.log | tail -10

How to diagnose it

  1. Verify the master process is alive. Check the PID file and confirm the process exists. If the PID file points to a stale or non-Postfix process, Postfix is in an inconsistent state.

    cat /var/spool/postfix/pid/master.pid
    ps -p $(cat /var/spool/postfix/pid/master.pid) -o pid,ppid,comm,start_time
    

    If master is not running, check why it stopped before restarting. Look for fatal or panic in the mail log. Verify there is no port conflict preventing startup.

    On systems using systemd, systemctl status postfix may show active (exited) even when the master process is not running. The actual process state is better checked via postfix status. On RHEL-family systems, also check systemctl status postfix@-.

  2. Check whether any socket exists on the target port. This distinguishes “nothing listening” from “listening but not responding.”

    ss -tlnp | grep -E ':25 |:587 '
    

    If no socket appears, the issue is in Postfix startup or binding. If a socket appears but belongs to a different process (sendmail, Dovecot, Exim), you have a port conflict.

  3. If no socket exists, check inet_interfaces and inet_protocols. The upstream default for inet_interfaces is all, but Debian and Ubuntu packages ship with inet_interfaces = localhost or loopback-only. If your host needs to receive mail from external clients, this must be all.

    postconf -h inet_interfaces
    postconf -h inet_protocols
    

    If inet_protocols = all but IPv6 is disabled on the host, Postfix may fail to bind with “cannot assign requested address.” Check the host’s IPv6 state:

    sysctl net.ipv6.conf.all.disable_ipv6
    

    If IPv6 is disabled, set inet_protocols = ipv4 and reload.

  4. If inet_interfaces and inet_protocols look correct, check master.cf for explicit IP bindings. An explicit IP in the service definition overrides inet_interfaces. The smtp service line should start with smtp inet, not 127.0.0.1:smtp inet.

    postconf -M smtp/inet
    postconf -M submission/inet
    
  5. If the socket is present but you get no 220 greeting, check smtpd process exhaustion. When all smtpd workers are consumed by slow clients (hung on DNS reverse lookups, milter timeouts, or content filter delays), new connections sit in the kernel queue but never get handled.

    # Count smtpd processes
    ps aux | grep smtpd | grep -v grep | wc -l
    
    # Check the configured process limit
    postconf -h default_process_limit
    
    # Look for "to limit" warnings in logs
    grep 'to limit' /var/log/mail.log | tail -20
    

    The default default_process_limit is 100. When the smtpd service maxproc in master.cf is -, it inherits default_process_limit. If your smtpd count is at or near 100 with no free workers, you need either more capacity, postscreen to shed zombie traffic, or investigation into what is holding smtpd processes.

  6. If postscreen is enabled, test both paths. Postscreen answers on port 25 and passes compliant connections to smtpd internally. If postscreen is listening but the smtpd handoff fails, clients see connection drops or no greeting.

    ps aux | grep postscreen | grep -v grep
    grep postscreen /var/log/mail.log | tail -20
    
  7. Test locally versus remotely to identify firewall interception. A socket that works on localhost but fails from an external host points to a firewall, cloud security group, or ISP block.

    # Local test
    echo QUIT | nc -w 5 localhost 25 | head -1
    
    # Remote test (run from another host)
    nc -zv -w 5 <mail-server-ip> 25
    

    Many cloud providers and residential ISPs block outbound port 25. This is not a Postfix issue, but it is the most common false positive for “Postfix not listening.”

  8. Verify the 220 greeting contains the configured hostname. A malformed greeting indicates a myhostname or configuration problem.

    # Check configured hostname
    postconf -h myhostname
    
    # Compare against actual greeting
    echo QUIT | nc -w 5 localhost 25 | head -1
    

    The greeting should contain the configured myhostname. If it does not, fix myhostname in main.cf and reload.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Master process livenessNo master means no mail at allPID file missing or points to wrong process
SMTP port listeningBinary availability checkSocket disappears from ss output
220 greeting response timeSocket present but smtpd exhaustedGreeting takes more than 2 seconds
smtpd process count vs maxprocIndicates worker pool saturationCount at or near default_process_limit
“to limit” log frequencyMaster cannot spawn new workersAny occurrence for smtpd or qmgr
Connection establishment latencyFirewall or network degradationExternal connections consistently slow or refused
Postscreen statistics (if enabled)Zombie traffic overwhelming smtpdHigh reject volume with concurrent smtpd saturation
Queue depth (active, deferred)Backlog from smtpd unavailabilityGrowing deferred queue with flat delivery rate

Fixes

Master process not running

First run postfix check to catch configuration errors that would prevent startup. Then start Postfix:

postfix start
# or
systemctl start postfix

If Postfix starts but immediately exits, check the mail log for fatal or panic messages. Common startup failures include syntax errors in main.cf or master.cf, permission problems on queue directories, and stale lock files. Remove stale PID files manually only after confirming no master process exists.

inet_interfaces bound to loopback

postconf -e 'inet_interfaces = all'
postfix reload

Note: postfix reload restarts child processes. Active connections will be interrupted.

After reloading, verify the socket is now on the external interface:

ss -tlnp | grep ':25 '

inet_protocols mismatch

If IPv6 is disabled on the host:

postconf -e 'inet_protocols = ipv4'
postfix reload

If IPv6 is enabled and you want dual-stack, leave inet_protocols = all but ensure the host actually has IPv6 connectivity. A partially configured IPv6 stack causes the same bind failure.

master.cf explicit IP binding

Edit /etc/postfix/master.cf and change the smtp service line from:

127.0.0.1:smtp inet n - y - - smtpd

to:

smtp inet n - y - - smtpd

Then reload. The explicit IP prefix overrides inet_interfaces, so removing it lets Postfix respect the inet_interfaces = all setting.

smtpd maxproc exhaustion

If smtpd processes are consistently at the limit, there are two levers:

  1. Increase the process limit (short-term relief):

    postconf -e 'default_process_limit = 200'
    postfix reload
    

    This affects all services, not just smtpd. Monitor memory and file descriptor usage after raising it. Alternatively, set maxproc only for the smtpd service in master.cf to avoid impacting other services.

  2. Enable postscreen (structural fix). Postscreen blocks known zombie clients before they consume an smtpd process. If you have high connection volume from scanning or dictionary-attack traffic, postscreen dramatically reduces smtpd pressure.

    Review the postscreen documentation for your version before enabling, as some configuration parameter names changed between Postfix releases.

Port conflict

Identify the conflicting process and either stop it or reconfigure it to listen on a different port:

ss -tlnp | grep -E ':(25|587) '

Common conflicts: Dovecot’s submission service on 587, a leftover sendmail or Exim process, or a container binding the same port.

Firewall or external block

If the socket works locally but not remotely, check in order:

  1. Host firewall: iptables -L -n or nft list ruleset
  2. Cloud security group or network ACL
  3. ISP or upstream provider port 25 blocking (common for residential and some cloud providers)

Port 25 blocking by ISPs is an infrastructure constraint, not a Postfix problem. If you need clients to reach you on a blocked port, use port 587 (submission) for authenticated clients or route through a relay.

Prevention

  • Monitor master process liveness continuously, not just via systemd unit status. Compare the PID file timestamp against the process start time to detect stale PID files after crashes.
  • Alert on SMTP port unavailability externally, not just locally. An external check catches firewall and network issues that a localhost check misses.
  • Track smtpd process count against maxproc. Approaching the limit is a leading indicator before connections start being refused.
  • Alert on any “to limit” log message for smtpd, qmgr, or cleanup. These are info-level and easy to miss without explicit alerting.
  • Review inet_interfaces and inet_protocols after every Postfix package update. Distribution updates can reset configuration to packaged defaults.
  • Evaluate postscreen for any internet-facing mail server. It is the most effective way to prevent smtpd pool exhaustion from zombie traffic.
  • Document the log file path for your distribution. Debian and Ubuntu use /var/log/mail.log; RHEL-family systems use /var/log/maillog. Monitoring and manual commands must target the correct file.

How Netdata helps

Netdata collects per-second metrics and correlates listener health with mail queue state and system signals, which narrows the failure mode quickly:

  • SMTP port liveness checked every second pinpoints when the socket disappears, correlating with deployments or process crashes.
  • Master process monitoring detects PID file staleness and process disappearance, distinguishing a crash from a clean shutdown.
  • smtpd process count tracked against default_process_limit reveals pool saturation before connections are refused.
  • Log pattern detection surfaces “to limit” warnings that are otherwise buried at info level in mail logs.
  • Queue depth metrics (active, deferred, incoming) correlate listener unavailability with queue growth, confirming whether smtpd exhaustion is the cause or a downstream symptom.
  • Connection establishment latency from external vantage points distinguishes firewall interception from Postfix-level failures.