Postfix SASL auth fails: the config gotchas and the fix order

Was this helpful?

You deploy a mail server that’s “basically working,” right up until users hit Send and their client gets punched in the face with 535 5.7.8 Authentication credentials invalid. Or worse: it fails intermittently, only for some clients, only after an upgrade, only on Tuesdays. Meanwhile your logs read like a ransom note written by a committee.

Postfix SASL failures are rarely “one setting wrong.” They’re usually a chain: TLS state affects whether AUTH is advertised, socket permissions decide whether Postfix can ask an auth daemon, and a single misleading test can convince you the wrong layer is broken. Fixing this quickly is about order: validate the listener, then TLS, then SASL plumbing, then user auth, then policy. Every time.

Fast diagnosis playbook

This is the “I’m on call and caffeine is not a monitoring system” playbook. The goal is to locate the bottleneck: network/listener, TLS gating, SASL plumbing, credential backend, or policy restrictions.

First: confirm you’re testing the right service and port

  • Check: Are you authenticating on submission (587) or smtps (465), not port 25?
  • Why: Many deployments intentionally disable AUTH on port 25 to avoid turning the MX into a brute-force endpoint.
  • Decide: If you’re using 25, stop. Switch the client to 587/465 and retest.

Second: verify AUTH is advertised under the same TLS state your client uses

  • Check: Does the server advertise AUTH only after STARTTLS? Or never?
  • Why: smtpd_tls_auth_only = yes is common and correct, but it confuses tests that don’t do STARTTLS.
  • Decide: If AUTH appears only after STARTTLS, ensure clients are configured for STARTTLS or implicit TLS on 465.

Third: identify which SASL provider is in play (Dovecot vs Cyrus vs saslauthd)

  • Check: Is Postfix set to smtpd_sasl_type = dovecot or cyrus?
  • Why: The error messages overlap, but the plumbing is different: Dovecot uses a UNIX socket; Cyrus often uses saslauthd or its own DB.
  • Decide: If the wrong provider is configured, fix that before touching passwords, PAM, LDAP, or anything “user-ish.”

Fourth: prove Postfix can reach the auth socket/daemon

  • Check: Socket path, permissions, SELinux/AppArmor, chroot path issues.
  • Decide: If Postfix can’t connect, don’t waste time on credentials. Fix connectivity and permissions first.

Fifth: validate the credential backend directly

  • Check: Can Dovecot authenticate the user? Can saslauthd authenticate via PAM/LDAP?
  • Decide: If the backend fails, fix the backend; Postfix is only the messenger (and it gets blamed for everything).

Sixth: confirm policy doesn’t reject “authenticated but not allowed”

  • Check: smtpd_sender_login_maps, recipient restrictions, relay restrictions, SPF/DMARC milter policies.
  • Decide: If AUTH succeeds but mail is still rejected, you have a policy block, not an auth block.

A production mental model: who authenticates whom

Postfix is an SMTP server and queue manager. It does not want to be your identity provider. When you enable SMTP AUTH, Postfix delegates authentication to a SASL layer. That SASL layer delegates again to something that actually validates credentials: a database, LDAP, PAM, SQL, Dovecot’s userdb/passdb, etc.

In practice there are two common shapes:

Shape A: Postfix + Dovecot SASL (most common on modern Linux)

  • Postfix runs the SMTP service (submission/smtps).
  • Postfix asks Dovecot to authenticate via a UNIX socket (the Dovecot auth service).
  • Dovecot checks credentials using its configured passdb/userdb (SQL, LDAP, passwd-file, PAM, etc.).

Typical failure modes: wrong socket path, permissions, chroot mismatch, Dovecot auth misconfigured, mechanism mismatch (PLAIN/LOGIN), TLS gating, or client using the wrong port.

Shape B: Postfix + Cyrus SASL (+ saslauthd or auxprop)

  • Postfix loads Cyrus SASL plugins.
  • Cyrus SASL queries saslauthd or its own auxprop backend.
  • saslpasswd2 or PAM/LDAP decides if credentials are correct.

Typical failure modes: missing Cyrus SASL packages, no mechanism available, saslauthd not running, wrong pwcheck_method, wrong socket permissions, or service name mismatch.

Regardless of shape, SMTP AUTH has two separate questions:

  1. Can the client and server negotiate an AUTH mechanism? (Capabilities, TLS requirements, supported mechanisms)
  2. Can the auth provider validate the user? (Backend correctness, password, account state)

Many outages happen because teams skip question 1 and start “resetting passwords” like it’s a ritual.

Facts and context you can weaponize

  • SMTP AUTH was not part of original SMTP. It arrived later as an extension (ESMTP), because the early Internet assumed trust inside networks.
  • Port 587 (submission) exists for a reason. It separates user mail submission (with AUTH) from MTA-to-MTA relay on port 25.
  • Port 465 came back from the dead. It was once “deprecated” but is now widely used for implicit TLS (“smtps”), because clients like simple.
  • Postfix intentionally keeps auth external. That’s a design choice: fewer security-critical moving parts inside Postfix, easier upgrades, and saner separation of duties.
  • SASL is a framework, not a single daemon. That’s why “SASL is broken” is rarely actionable; you have to name the provider and backend.
  • AUTH advertisement depends on TLS and restrictions. With common settings, the server can hide AUTH until STARTTLS happens, making naive tests lie.
  • In Dovecot, the auth socket is a service with permissions. If the socket exists but Postfix can’t access it, you get failures that look like “wrong password.”
  • Chroot still matters in 2026. Postfix can run some services in a chroot; a socket path outside the chroot becomes “not found” even when it exists.
  • “Anonymous TLS cipher mismatch” can manifest as “AUTH not offered.” If TLS negotiation fails, you may never reach the point where AUTH is advertised.

One quote, because reliability folks were yelling about this before it was trendy:

“Hope is not a strategy.” — General Gordon R. Sullivan

The fix order (do this, not vibes)

If you change settings randomly, you’ll “fix” it three times and still not know why it broke. Use an order that isolates layers and keeps you from chasing ghosts.

0) Freeze the scene

Before you edit anything, capture: current configs, package versions, and representative logs. SMTP AUTH bugs love to disappear under reconfiguration noise.

1) Identify the exact entry point: service, port, and hostname

Is the client hitting the intended host (submission frontend vs MX)? Is it hitting the right port? Is there a load balancer doing STARTTLS termination? You’d be amazed how often “AUTH fails” is actually “you connected to the MX pool that has no AUTH by policy.”

2) Confirm capability negotiation (EHLO → STARTTLS → EHLO → AUTH)

Don’t debug SASL before you know whether AUTH is even offered. If AUTH isn’t offered, it’s usually one of:

  • Wrong service/port
  • smtpd_sasl_auth_enable not enabled for that service
  • TLS required (smtpd_tls_auth_only) but you’re not using STARTTLS
  • Restrictions hide AUTH
  • Misconfigured SASL provider prevents mechanism listing

3) Lock down TLS correctness

Fix TLS before auth. If TLS fails, some clients won’t even try AUTH. Others will try and leak credentials if you accidentally allow AUTH in cleartext. You don’t want that on your conscience or your audit log.

4) Confirm SASL provider wiring (Dovecot or Cyrus)

This is where most “it worked on the old server” migrations fall apart. Postfix needs the right SASL type and correct socket/params. If you’re using Dovecot, treat the auth socket like an API endpoint: path, permissions, and protocol must match.

5) Validate backend auth outside SMTP

Test Dovecot’s auth directly (or saslauthd) to isolate credentials and backend configuration. If backend auth fails, Postfix cannot fix it with positive thinking.

6) Apply policy deliberately after AUTH works

Once AUTH succeeds, then enforce your rules: authenticated users can relay; unauthenticated cannot; sender restrictions; rate limits. If you do policy first, you’ll confuse “auth failed” with “auth succeeded but not authorized.”

Short joke #1: SMTP AUTH debugging is like plumbing: the leak is never where the puddle is.

Practical tasks: commands, outputs, decisions

These are the tasks I actually run in production. Each includes: command, what the output means, and the decision you make next. Run them on the server you believe is handling submission, not “somewhere near it.”

Task 1: Confirm Postfix is running and which services are enabled

cr0x@server:~$ systemctl status postfix --no-pager
● postfix.service - Postfix Mail Transport Agent
     Loaded: loaded (/lib/systemd/system/postfix.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2026-01-02 09:12:44 UTC; 3h 18min ago
       Docs: man:postfix(1)
   Main PID: 1221 (master)
      Tasks: 5 (limit: 18996)
     Memory: 28.4M
        CPU: 2.114s
     CGroup: /system.slice/postfix.service
             ├─1221 /usr/lib/postfix/sbin/master -w
             ├─1230 pickup -l -t unix -u
             ├─1231 qmgr -l -t unix -u
             ├─1302 smtpd -n smtp -t inet -u -c
             └─1303 tlsmgr -t unix -u

What it means: Master is running; there’s at least one smtpd. But you still don’t know if submission (587) is enabled.

Decision: Check listeners and master.cf next.

Task 2: Verify the server is listening on the right ports (25/587/465)

cr0x@server:~$ sudo ss -lntp | egrep ':(25|465|587)\s'
LISTEN 0      100          0.0.0.0:25       0.0.0.0:*    users:(("master",pid=1221,fd=13))
LISTEN 0      100          0.0.0.0:587      0.0.0.0:*    users:(("master",pid=1221,fd=16))
LISTEN 0      100          0.0.0.0:465      0.0.0.0:*    users:(("master",pid=1221,fd=18))

What it means: Postfix is listening on 587 and 465. Good. If 587/465 were missing, your problem is not SASL; it’s service enablement or firewall.

Decision: If 587/465 aren’t listening, fix master.cf and firewall before touching SASL.

Task 3: Confirm how submission is defined in master.cf

cr0x@server:~$ sudo postconf -Mf | egrep '^(submission|smtps)\b'
submission inet n       -       y       -       -       smtpd
smtps     inet  n       -       y       -       -       smtpd

What it means: Services exist. But overrides (like -o smtpd_sasl_auth_enable=yes) may or may not be present.

Decision: Dump the exact service stanza and verify SASL/TLS overrides.

Task 4: Show the effective Postfix parameters relevant to SASL/TLS

cr0x@server:~$ sudo postconf -n | egrep '^(smtpd_sasl|smtpd_tls|broken_sasl_auth_clients|smtpd_recipient_restrictions|smtpd_relay_restrictions|smtpd_sender_login_maps|smtpd_sasl_security_options|smtpd_sasl_path|smtpd_sasl_type)\b'
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
broken_sasl_auth_clients = yes
smtpd_relay_restrictions = permit_mynetworks,permit_sasl_authenticated,defer_unauth_destination

What it means: Postfix is configured to use Dovecot SASL via private/auth. AUTH will be offered only after TLS due to smtpd_tls_auth_only=yes. Relay is allowed for authenticated users.

Decision: Your tests must use STARTTLS (587) or implicit TLS (465). Next: prove AUTH is advertised after TLS.

Task 5: Probe capabilities without TLS (expect AUTH hidden)

cr0x@server:~$ printf 'EHLO test\r\nQUIT\r\n' | nc -w 3 127.0.0.1 587
220 mail01.example.net ESMTP Postfix
250-mail01.example.net
250-PIPELINING
250-SIZE 10240000
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 SMTPUTF8
221 2.0.0 Bye

What it means: No AUTH advertised. That’s consistent with smtpd_tls_auth_only=yes.

Decision: Test with STARTTLS and EHLO again.

Task 6: Probe capabilities with STARTTLS (AUTH should appear now)

cr0x@server:~$ openssl s_client -starttls smtp -crlf -connect 127.0.0.1:587 </dev/null
CONNECTED(00000003)
depth=2 C=US O=Example CA CN=Example Root CA
verify return:1
depth=1 C=US O=Example CA CN=Example Issuing CA
verify return:1
depth=0 CN=mail01.example.net
verify return:1
---
250-mail01.example.net
250-PIPELINING
250-SIZE 10240000
250-ETRN
250-AUTH PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 SMTPUTF8

What it means: STARTTLS works, and AUTH mechanisms are advertised. If your client claims “server does not support authentication,” it’s either not using TLS or it’s on the wrong port/host.

Decision: If AUTH is not advertised even after STARTTLS, jump to SASL provider plumbing (socket/daemon/mechanisms).

Task 7: Send a real AUTH test using swaks (best bang-for-buck)

cr0x@server:~$ swaks --server 127.0.0.1 --port 587 --tls --auth LOGIN --auth-user alice@example.net --auth-password 'nottherightpassword' --quit-after AUTH
=== Trying 127.0.0.1:587...
=== Connected to 127.0.0.1.
<--  220 mail01.example.net ESMTP Postfix
 --> EHLO client.example
<--  250-mail01.example.net
<--  250-STARTTLS
 --> STARTTLS
<--  220 2.0.0 Ready to start TLS
=== TLS started with cipher TLS_AES_256_GCM_SHA384
 --> EHLO client.example
<--  250-AUTH PLAIN LOGIN
 --> AUTH LOGIN
<--  334 VXNlcm5hbWU6
 --> YWxpY2VAZXhhbXBsZS5uZXQ=
<--  334 UGFzc3dvcmQ6
 --> bm90dGhlcmlnaHRwYXNzd29yZA==
<--  535 5.7.8 Error: authentication failed: authentication failure

What it means: You got a clean 535. That’s not a SASL plumbing failure; it’s credentials/backend or Dovecot auth rules. If instead you saw 454 4.7.0 Temporary authentication failure, think socket/daemon availability.

Decision: With a stable 535, test backend auth directly (Dovecot). With 454, check socket permissions/daemon health.

Task 8: Inspect Postfix log lines tied to the failure

cr0x@server:~$ sudo journalctl -u postfix -S "10 min ago" --no-pager | egrep -i 'sasl|auth|warning|fatal' | tail -n 30
Jan 03 10:41:12 mail01 postfix/smtpd[18422]: warning: SASL authentication failure: Password verification failed
Jan 03 10:41:12 mail01 postfix/smtpd[18422]: warning: unknown[127.0.0.1]: SASL LOGIN authentication failed: authentication failure

What it means: Postfix reached the SASL layer and got a definitive “password verification failed.” That’s not a missing socket. It’s an auth backend or credential issue.

Decision: Move to Dovecot logs and Dovecot auth tests.

Task 9: Confirm Dovecot auth socket is where Postfix expects it

cr0x@server:~$ sudo doveconf -n | egrep '^(service auth|  unix_listener|    mode|    user|    group|auth_mechanisms)'
auth_mechanisms = plain login
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

What it means: This is the gold standard for Dovecot+Postfix: socket lives under /var/spool/postfix/private so chrooted Postfix can reach it; owned by postfix; mode allows access.

Decision: If your socket is elsewhere (like /run/dovecot/auth-client), either move it or adjust Postfix chroot behavior. Don’t “just chmod 777” anything.

Task 10: Verify the socket exists and permissions are sane

cr0x@server:~$ sudo ls -l /var/spool/postfix/private/auth
srw-rw---- 1 postfix postfix 0 Jan  3 10:02 /var/spool/postfix/private/auth

What it means: It exists, it’s a socket, and postfix can access it.

Decision: If it’s missing: Dovecot not running, wrong path, or listener not configured. If permissions are wrong: fix in Dovecot config and reload Dovecot.

Task 11: Test Dovecot authentication directly (bypass Postfix)

cr0x@server:~$ sudo doveadm auth test alice@example.net 'nottherightpassword'
passdb: alice@example.net auth failed
extra fields:
  user=alice@example.net

What it means: Backend says no. That’s consistent with the 535. If this fails for correct credentials too, your passdb configuration is wrong (SQL/LDAP/PAM), or the account is disabled, or you’re using the wrong username format.

Decision: Fix Dovecot passdb/userdb or the account data. Don’t touch Postfix until Dovecot auth passes.

Task 12: Look at Dovecot’s auth logs for the real reason

cr0x@server:~$ sudo journalctl -u dovecot -S "10 min ago" --no-pager | egrep -i 'auth|passdb|userdb|pam|ldap' | tail -n 40
Jan 03 10:41:12 mail01 dovecot[903]: auth: passwd-file(alice@example.net): Password mismatch
Jan 03 10:41:12 mail01 dovecot[903]: auth: Error: passwd-file: Cannot open file /etc/dovecot/users: Permission denied

What it means: Now we’re getting somewhere: Dovecot can’t read its passwd-file. Postfix wasn’t lying; it just didn’t have the context.

Decision: Fix file permissions/ownership/SELinux labels for /etc/dovecot/users or adjust Dovecot to read from a proper backend.

Task 13: Check for Postfix chroot gotcha (socket path inside /var/spool/postfix)

cr0x@server:~$ sudo postconf -n | egrep '^(queue_directory|daemon_directory|command_directory)\b'
queue_directory = /var/spool/postfix
daemon_directory = /usr/lib/postfix/sbin
command_directory = /usr/sbin

What it means: Postfix queue is in /var/spool/postfix. Many distros chroot some services there. If your Dovecot socket is outside that tree, Postfix may not see it.

Decision: Prefer Dovecot socket under /var/spool/postfix/private/ as shown earlier.

Task 14: If using Cyrus SASL, list available mechanisms and spot “no mechanism available”

cr0x@server:~$ sudo saslpluginviewer -c
Installed and properly configured SASL (Cyrus SASL) mechanisms are:
  PLAIN
  LOGIN

What it means: Mechanisms exist. If this list is empty, Postfix will often log no mechanism available and clients will see AUTH fail or vanish.

Decision: Install the right Cyrus SASL mechanism packages and confirm the service name config (often in /etc/postfix/sasl/smtpd.conf).

Task 15: Confirm saslauthd is running and reachable (Cyrus path)

cr0x@server:~$ systemctl status saslauthd --no-pager
● saslauthd.service - SASL authentication daemon
     Loaded: loaded (/lib/systemd/system/saslauthd.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2026-01-02 09:10:01 UTC; 3h 21min ago
   Main PID: 991 (saslauthd)
      Tasks: 5 (limit: 18996)
     Memory: 4.7M
        CPU: 1.221s
     CGroup: /system.slice/saslauthd.service
             ├─991 /usr/sbin/saslauthd -a pam -m /var/run/saslauthd
             ├─992 /usr/sbin/saslauthd -a pam -m /var/run/saslauthd
             └─993 /usr/sbin/saslauthd -a pam -m /var/run/saslauthd

What it means: It’s alive and using PAM with socket directory /var/run/saslauthd.

Decision: If Postfix can’t talk to it, check that postfix user is in the correct group (commonly sasl) and that socket permissions allow access.

Task 16: Identify “AUTH works but relay denied” vs “AUTH failed”

cr0x@server:~$ swaks --server 127.0.0.1 --port 587 --tls --auth LOGIN --auth-user alice@example.net --auth-password 'correctpassword' --to bob@external.example --from alice@example.net
...
<--  235 2.7.0 Authentication successful
...
<--  554 5.7.1 <bob@external.example>: Relay access denied

What it means: AUTH succeeded (235) but relay is denied. That’s authorization/policy. People often misread this as “auth broken.” It isn’t.

Decision: Review smtpd_relay_restrictions and ensure permit_sasl_authenticated is in the right place for the submission service, not overridden accidentally.

Short joke #2: Every time someone says “it’s just mail,” an SMTP daemon spawns another log file.

Three corporate mini-stories from the trenches

Mini-story 1: The incident caused by a wrong assumption

The setup: a mid-sized company with a hybrid environment. They had a Postfix submission server for clients, and a separate MX pool for inbound mail. The DNS and naming were tidy: smtp.example.net for submission, mx1/mx2 for inbound. On paper, nobody should confuse them.

Then a new MDM profile rolled out for mobile devices. Someone fat-fingered the server name field and set it to the MX hostname. The MX hosts had port 25 open (obviously) and TLS enabled (also obviously), but they were configured to never advertise AUTH on port 25. This is normal and sane: port 25 is the front door for the Internet, not your employees.

Within minutes, support tickets arrived: “password rejected.” Engineers pulled Postfix logs from the submission server and saw nothing. They pulled logs from the MX and found lots of connections, no AUTH attempts, and clients complaining about authentication. The team’s first assumption was “SASL is down.” So they restarted Dovecot on the submission server. Then they restarted Postfix. Then they restarted everything else within arm’s reach.

Nothing changed, because nothing was wrong on the submission server. The wrong assumption was that the traffic was landing where the team expected. Once they confirmed with a packet capture and the MX logs that clients were connecting to the MX, the fix was embarrassingly simple: update the MDM profile to point to smtp.example.net and require port 587.

Two lessons stuck: first, always verify the target host and port before debugging. Second, “AUTH not offered” is not the same as “AUTH failed.” It’s frequently “you’re at the wrong door.”

Mini-story 2: The optimization that backfired

A different shop wanted to tighten security and reduce attack surface. Great impulse. They decided to enforce TLS everywhere, and they flipped smtpd_tls_security_level=encrypt globally. They also enabled smtpd_tls_auth_only=yes. In isolation, both are reasonable.

The backfire came from a detail: they had an internal monitoring job and a few legacy applications that sent mail to the submission service without STARTTLS support. Those apps were on a “trusted” VLAN, and historically the submission service allowed plain connections from that subnet. Not ideal, but it worked and the risk was bounded. The change turned “bounded risk” into “full outage,” and nobody had inventory of those legacy clients because the owners had changed teams three times.

The symptoms were confusing. Some clients worked (modern MUAs). Others failed with messages that looked like auth failures or handshake failures depending on the library. The on-call engineer focused on SASL because “mail send fails” usually gets that label.

The correct diagnosis was capability negotiation. Those legacy apps never got to AUTH; they couldn’t establish TLS. The fix wasn’t to weaken TLS globally. It was to apply policy per service: keep submission strict, but provide a separate internal relay with explicit network restrictions and no AUTH, or require those apps to use a local relay that handles TLS upstream.

The outcome: security improved, but only after a painful reminder that tightening screws on production systems without a client inventory is just performance art.

Mini-story 3: The boring but correct practice that saved the day

A finance-adjacent company had a mail platform where “boring” was a compliment. They kept a runbook that insisted on one thing before touching configs: reproduce with swaks and capture the full SMTP transcript. No exceptions. People rolled their eyes until they didn’t.

One Friday, after a routine OS upgrade, AUTH started failing for a subset of users, but only when connecting through a particular VIP. The immediate suspicion was load balancer weirdness, certificate mismatch, or a broken SASL socket. The on-call engineer followed the runbook, ran swaks against each backend and against the VIP, and compared transcripts side-by-side.

The transcript showed that one backend advertised only AUTH PLAIN while others advertised AUTH PLAIN LOGIN. Some clients were hardcoded to try LOGIN first, then gave up if LOGIN was missing. This wasn’t theoretical; it was an old mobile client deployed in bulk.

The root cause ended up being a partial config drift: a single node had a different Dovecot auth_mechanisms setting due to a failed automation run weeks earlier. The OS upgrade simply triggered a reload that made the drift visible.

The boring practice—always capture and compare the SMTP transcript—turned a potentially long outage into a straightforward “make the nodes consistent” fix. Nobody wrote a victory email. Everyone went home.

Common mistakes: symptom → root cause → fix

These are the failure modes that keep repeating across environments. If you recognize the symptom, don’t freestyle. Follow the fix in order.

1) Symptom: “Server does not support authentication” (client UI)

Root cause: You’re on port 25, or AUTH is hidden until STARTTLS, or you’re hitting the MX not submission.

Fix: Use port 587 with STARTTLS or port 465 with implicit TLS. Verify AUTH appears only after STARTTLS using openssl s_client -starttls smtp. Confirm hostname and VIP routing.

2) Symptom: AUTH disappears after enabling TLS settings

Root cause: TLS handshake failing, so second EHLO never happens; or TLS policy requires encryption but your probe doesn’t STARTTLS.

Fix: Fix certificates/chain, set correct smtpd_tls_cert_file/key_file, and retest with STARTTLS. Don’t test with plaintext and conclude SASL is broken.

3) Symptom: 454 4.7.0 Temporary authentication failure

Root cause: Postfix cannot reach the SASL provider (Dovecot socket missing/permission denied) or the provider is down.

Fix: For Dovecot, ensure service auth socket exists under /var/spool/postfix/private/auth, correct ownership/mode, and Dovecot is running. For Cyrus, ensure saslauthd is running and accessible.

4) Symptom: 535 5.7.8 Authentication credentials invalid

Root cause: Bad password, wrong username format, backend mismatch, or account disabled/locked.

Fix: Test with doveadm auth test (or saslauthd test tools) using the exact username the client sends. Fix the backend. Avoid “fixing” Postfix.

5) Symptom: Postfix logs warning: SASL: Connect to private/auth failed: No such file or directory

Root cause: Socket path mismatch or chroot mismatch. Postfix expects private/auth relative to its queue directory; Dovecot put the socket elsewhere.

Fix: Configure Dovecot’s auth socket at /var/spool/postfix/private/auth and set correct permissions. Or adjust Postfix smtpd_sasl_path to match, but prefer the standard layout.

6) Symptom: Postfix logs no mechanism available

Root cause: Cyrus SASL plugins not installed, or Dovecot mechanisms not enabled.

Fix: Install mechanism packages for Cyrus, or set Dovecot auth_mechanisms = plain login. Confirm with mechanism listing and STARTTLS EHLO output.

7) Symptom: AUTH succeeds but messages are rejected as relay denied

Root cause: Authorization policy: permit_sasl_authenticated missing/overridden on submission service, or relay restrictions are wrong.

Fix: Put permit_sasl_authenticated in smtpd_relay_restrictions (or appropriate restrictions for your Postfix version) for submission. Confirm with a swaks send.

8) Symptom: Works for some users, fails for others (same client)

Root cause: Backend inconsistency (replication lag, multiple auth sources), username normalization issues, or per-user policy like sender login maps.

Fix: Validate authentication and authorization separately. Check smtpd_sender_login_maps behavior and Dovecot passdb/userdb lookup for each user.

Checklists / step-by-step plan

Checklist A: Make AUTH appear reliably on submission (587)

  1. Ensure submission service is enabled in master.cf and listening on 587.
  2. Set per-service overrides for submission (recommended):
    • smtpd_tls_security_level=encrypt (or may if you must, but know why)
    • smtpd_sasl_auth_enable=yes
    • smtpd_tls_auth_only=yes
  3. Verify STARTTLS works and certificate chain is sane.
  4. Verify post-STARTTLS EHLO advertises AUTH PLAIN LOGIN (or the mechanisms you intend).

Checklist B: Dovecot SASL wiring that doesn’t age badly

  1. In Postfix:
    • smtpd_sasl_type = dovecot
    • smtpd_sasl_path = private/auth
  2. In Dovecot:
    • auth_mechanisms = plain login (unless you know you need others)
    • service auth { unix_listener /var/spool/postfix/private/auth { user=postfix group=postfix mode=0660 } }
  3. Confirm socket exists and is accessible.
  4. Use doveadm auth test for a known-good user before testing SMTP AUTH.

Checklist C: Cyrus SASL wiring (if you insist)

  1. Confirm mechanisms exist (saslpluginviewer not empty).
  2. Confirm saslauthd runs and uses the backend you intend (PAM/LDAP/etc.).
  3. Ensure Postfix can access saslauthd socket directory (group membership/permissions).
  4. Confirm your Cyrus SASL service config (often /etc/postfix/sasl/smtpd.conf) matches your desired pwcheck_method.

Step-by-step “fix order” plan you can follow during an outage

  1. Reproduce with swaks against the exact hostname/port users hit. Save transcript.
  2. Check listener with ss -lntp. If not listening, fix that first.
  3. Check EHLO capabilities pre- and post-TLS. If AUTH doesn’t appear post-TLS, don’t touch passwords.
  4. Confirm SASL provider settings (postconf -n). Decide Dovecot vs Cyrus; remove ambiguity.
  5. Validate socket/daemon reachability (socket exists, perms, chroot, MAC policy).
  6. Validate backend auth directly (doveadm auth test or saslauthd tooling).
  7. Only then tune policy restrictions and sender login maps.
  8. Retest with swaks, then retest with a real client.

FAQ

1) Should I enable SMTP AUTH on port 25?

Usually no. Keep port 25 for MTA-to-MTA traffic. Put AUTH on 587/465. If you must enable AUTH on 25 for a special case, isolate by IP and rate-limit aggressively.

2) Why does AUTH show up only after STARTTLS?

Because smtpd_tls_auth_only=yes tells Postfix to advertise AUTH only after encryption is active. This prevents credentials from being offered in the clear. If your test doesn’t STARTTLS, you’ll falsely conclude AUTH is disabled.

3) What’s the difference between 465 and 587 for clients?

587 is submission with STARTTLS (plaintext connect, then upgrade). 465 is implicit TLS from the first byte. Operationally both can be fine; many environments run both because client behavior is unpredictable.

4) I see 535. Is that always a wrong password?

It’s “credentials rejected” at some layer, but not always the human’s password. It can be wrong username format, backend lookup failing, an account lock, or a mismatched auth database between nodes. Test with doveadm auth test (or your backend’s native check) to be sure.

5) I see 454 4.7.0 Temporary authentication failure. What does that imply?

That’s usually plumbing: Postfix can’t talk to the auth provider, or the provider can’t reach its backend. Think missing socket, permission denied, daemon down, chroot/SELinux issues. It’s rarely “user typed password wrong.”

6) Dovecot auth works, but SMTP AUTH still fails. How?

Common reasons: Postfix points to the wrong socket path, Postfix is chrooted and can’t see the socket, permissions are wrong for the postfix user, or the submission service overrides differ from main.cf defaults. Validate smtpd_sasl_type, smtpd_sasl_path, and the existence of /var/spool/postfix/private/auth.

7) Why do some clients fail when LOGIN isn’t offered?

Some older clients don’t handle mechanism negotiation gracefully. If you only offer PLAIN, they may not try it (even though PLAIN over TLS is acceptable). If you need broad compatibility, offer both PLAIN and LOGIN over TLS.

8) Is “broken_sasl_auth_clients” still needed?

Sometimes. It exists for buggy client implementations that don’t follow the spec perfectly. If you don’t need it, you can remove it, but don’t rip it out mid-incident unless you’ve reproduced a failure tied to it.

9) How do I tell “AUTH failed” from “AUTH succeeded but not allowed to send as that address”?

Look for 235 Authentication successful first. If you get 235 and then rejections like relay denied or sender access denied, you’re in authorization/policy land: smtpd_sender_login_maps, restrictions, or milters.

10) Should I prefer Dovecot SASL or Cyrus SASL?

If you already run Dovecot for IMAP/POP, use Dovecot SASL. Fewer moving parts, fewer package/plugin surprises, and the socket model is straightforward. Cyrus SASL can be fine, but it’s easier to accidentally build a fragile stack.

Next steps you can ship

Postfix SASL auth failures are solvable quickly when you stop guessing and start isolating layers. Treat it like any other production dependency chain: verify the entry point, confirm negotiation, validate the auth plumbing, then validate the backend, then enforce policy.

Practical next steps:

  1. Capture a known-good swaks transcript for your environment and keep it in the runbook as the baseline.
  2. Standardize on one SASL provider (Dovecot if you run it) and one socket path (/var/spool/postfix/private/auth).
  3. Make submission policy explicit in master.cf overrides so upgrades don’t “helpfully” change behavior.
  4. Add a monitoring check that verifies: STARTTLS works, AUTH is advertised after TLS, and a synthetic AUTH succeeds for a test account.
← Previous
Coil whine: why your GPU squeals—and what you can (and can’t) do
Next →
A driver update killed my FPS: how to diagnose it properly

Leave a comment