• The APS Catalog has been deprecated and removed from all Plesk Obsidian versions.
    Applications already installed from the APS Catalog will continue working. However, Plesk will no longer provide support for APS applications.
  • Please be aware: with the Plesk Obsidian 18.0.78 release, the support for the ngx_pagespeed.so module will be deprecated and removed from the sw-nginx package.

Resolved Dovecot 2.4.1-4 and 2.4.2 silently ignore administrator-scoped mailbox { sieve_script } blocks for IMAP events

stephan.kurz

New Pleskian
Server operating system version
Ubuntu 24.04 x86_64
Plesk version and microupdate number
18.0.77 #2
PRODUCT, VERSION, OPERATING SYSTEM, ARCHITECTURE
Product version: Plesk Obsidian 18.0.77.2 (also reproduced on 18.0.74.3)
OS version: Ubuntu 24.04 x86_64
Dovecot: plesk-dovecot-core 2.4.2 (also 2.4.1-4 on 18.0.74.3)
Pigeonhole: plesk-dovecot-pigeonhole 2.4.2

PROBLEM DESCRIPTION
An administrator-scoped sieve_script placed inside a mailbox { } block, using the documented Dovecot 2.4 syntax, parses cleanly and loads without errors. Dovecot starts, systemctl reports active, doveconf -n shows the block. IMAP COPY/APPEND events on the target mailbox occur normally. But the sieve_script inside the mailbox block never fires — no execution log entries, no script side effects.

Goal: use imap_sieve for rspamd Bayes feedback — move INBOX → Spam fires learn_spam, move Spam → INBOX fires learn_ham. Standard pattern documented at imap-sieve | Dovecot CE

STEPS TO REPRODUCE
1. Pre-compile sieve scripts at /etc/dovecot/sieve/report-spam.svbin using sievec. Verify the .svbin is readable by the imap process.
2. Add to /etc/dovecot/conf.d/99-custom.conf:

protocol imap {
mail_plugins {
imap_sieve = yes
}
}

sieve_plugins {
sieve_imapsieve = yes
sieve_extprograms = yes
}

sieve_extensions {
vnd.dovecot.pipe = yes
}

namespace inbox {
mailbox "INBOX.Spam" {
sieve_script learn_spam {
type = before
cause = copy
path = /etc/dovecot/sieve/report-spam.svbin
}
}
}

3. doveconf -n — no errors, block appears in output.
4. systemctl restart dovecot — starts cleanly, active (running).
5. From a real IMAP client (tested Thunderbird, Apple Mail), move a message from INBOX to INBOX.Spam.
6. Observe /var/log/maillog — IMAP COPY event visible, but no sieve execution entries. No script side effects.

Also tried mailbox Spam { ... } (namespace-internal name, no INBOX. prefix) — same silent no-op. Note: doveadm copy does NOT reproduce — imap_sieve fires only on real IMAP client sessions.

EXPECTED RESULT
The sieve_script inside the mailbox block executes on IMAP COPY events to the matched mailbox, per the Dovecot 2.4 IMAPSieve documentation. The script should run, produce log entries, and invoke the configured pipe command.

ANY ADDITIONAL INFORMATION
Same symptom reproduces independently of Plesk packaging:
This suggests a Pigeonhole 2.4.x settings-handling issue for administrator-scoped mailbox blocks, not a Plesk packaging issue. But Plesk is the update path for this server, and any fix will need to land via plesk-dovecot-pigeonhole package updates.

Distinct from PPPM-15312 (Dovecot crash when imap_sieve=yes, triggered by mail_attachment_detection_options default change in 2.4.2, fixed in 18.0.76 Update 2). That is a separate bug — it's a crash; this is a clean-startup, silent-no-op-at-event-time bug.

The 2.3-era imapsieve_mailboxN_* keys are hard-rejected on Dovecot 2.4 as expected ("Unknown setting"), confirming the 2.3 syntax is not a fallback.
Config has been reverted on this server. Happy to re-enable temporarily for any reproduction steps Plesk staff want to verify. doveconf -n output available on request.

EXPECTATIONS FROM PLESK SERVICE TEAM
1. Confirm reproduction on a Plesk Obsidian 18.0.77.2 + Ubuntu 24.04 server.
2. Track as a separate bug from PPPM-15312.
3. Coordinate with Dovecot / Pigeonhole upstream if root cause is confirmed in plugin settings handling.
4. Consider bumping plesk-dovecot-pigeonhole to 2.4.3 in a future Plesk release — upstream 2.4.3 has settings-handling fixes that may address this; worth retesting.
 
What I am missing from your post is how you've determined this is an issue actually caused by dovecot? I get that your not getting the expected result; rspamd is not learning when a message is moved to the spam folder. But how are you sure the sieve_script never gets called? Could it be that the script does get called, but could not be executed? What have you done to troubleshoot the issue?
 
Kaspar — thank you for the pushback. You were right that the original report didn't separate the four failure modes, and re-running with proper instrumentation showed I was wrong about the diagnosis. Posting the retest data here in case it's useful for anyone hitting a similar shape of "silent" failure & i'm asking the moderators to close this as not-a-bug.

What I changed for the retest

Wrapped both pipe scripts (/usr/local/lib/dovecot-sieve-pipe/rspamd-learn-{spam,ham}.sh) with a tee-based trace that captures invocation metadata (timestamp, pid, ppid, uid, gid, argv, exit code, stdin byte count, rspamc stdout/stderr) AND a per-invocation copy of the full stdin payload, without breaking rspamc's stdin consumption. This was the missing piece — without instrumenting the script itself, "no rspamd Bayes change" and "script never executed" are observationally identical.

Also added log_debug = category=sieve OR category=imap-sieve OR category=sieve-storage in a temporary drop-in to surface Pigeonhole's per-event chatter.

Configuration was the same canonical 2.4.3 form from the original report:

Code:
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_pipe_bin_dir = /usr/local/lib/dovecot-sieve-pipe
sieve_extensions { vnd.dovecot.pipe = yes }

protocol imap {
  mail_plugins {
    imap_sieve = yes
  }
}

mailbox Spam {
  sieve_script report-spam {
    type = before
    cause = copy
    path = /etc/dovecot/sieve/report-spam.sieve
  }
}

imapsieve_from Spam {
  sieve_script report-ham {
    type = before
    cause = copy
    path = /etc/dovecot/sieve/report-ham.sieve
  }
}

Test client: Roundcube doing a real IMAP MOVE (Inbox → Spam folder) on the test mailbox. Two-phase test:

  • Control move first: Inbox → freshly-created scratch folder (not configured in any mailbox or imapsieve_from block). Expectation: pipe stays silent.
  • Test move after: Inbox → Spam (configured target). Expectation: pipe fires.

Result — pipe fired correctly on Dovecot 2.4.2 / Plesk 18.0.77.2

Control phase: zero pipe-script invocations, zero rspamd activity. Working as expected.

Test phase, captured across three streams:

Pipe-script trace (the instrumentation file):
Code:
[2026-04-24T13:22:07+00:00] ENTER script=.../rspamd-learn-spam.sh pid=310978 ppid=310977 uid=30 gid=31 argv=[[email protected]]
[2026-04-24T13:22:07+00:00] EXIT  pid=310978 rc=0 stdin_bytes=418 rspamc_out=[Results for file: stdin (0.003 seconds) success = true; filename = "stdin"; scan_time = 0.003000; ]

Dovecot journal (sieve execution chain, abridged):
Code:
... Mailbox INBOX.Spam: imapsieve: storage report-spam: file: Using Sieve script path: /etc/dovecot/sieve/report-spam.sieve
... sieve: Script binary /etc/dovecot/sieve/report-spam.svbin successfully loaded
... sieve: Started running script '/etc/dovecot/sieve/report-spam.svbin'
... sieve: Executing pipe action
... sieve: action pipe: running program: rspamd-learn-spam.sh
... sieve: execute fork:.../rspamd-learn-spam.sh: Created ([email protected])
... sieve: execute exec:.../rspamd-learn-spam.sh (310978): Connected to program
... sieve: execute exec:.../rspamd-learn-spam.sh (310978): Finished streaming payload to program
... sieve: execute exec:.../rspamd-learn-spam.sh (310978): Child process ended
... sieve: uid=7: pipe action: piped message to program 'rspamd-learn-spam.sh'
... sieve: uid=7: left message in mailbox 'INBOX.Spam'

Rspamd controller log:
Code:
... csession; rspamd_message_parse: loaded message; id: <[email protected]>; size: 418
... csession; rspamd_controller_learn_fin_task: <127.0.0.1> learned message as spam: [email protected]

Counter delta: rspamc stat showed Messages learned: 0 → 1.

What ruled out each failure mode

Failure modeRuled out by
1. Sieve script never invokedPipe-trace ENTER line present + journal execute fork: ... rspamd-learn-spam.sh: Created
2. Plugin loaded silently failedJournal shows Sieve imapsieve plugin ... loaded at master start AND per-session, plus full per-event trace through Executing pipe actionFinished streaming payload
3. Pipe script ran but failedEXIT line shows rc=0, rspamc returned success = true
4. Pipe ran, rspamd rejected the learnRspamd controller log shows learned message as spam, Messages learned counter incremented

What went wrong on my previous attempts

No raw transcripts from the earlier sessions, so this is reconstruction not proof. Most plausible cause: those tests used a path that bypasses the IMAP protocol layer — likely doveadm copy/move/save, which operates against the mailbox storage API directly and doesn't fire imap_sieve hooks. From the outside, a doveadm-based test looks identical to a real IMAP test that hit a broken imap_sieve: the message lands in the destination folder ("the move worked"), and the rspamd Bayes counter doesn't move ("the script didn't fire"). Without instrumenting the pipe script itself, the two scenarios are observationally indistinguishable.

Asking moderators to close

Please close this thread as not-a-bug. The wiring works as documented in doc.dovecot.org/2.4.3 — spam reporting on Plesk's shipped Dovecot 2.4.2. No fix needed in lib95_imap_sieve_plugin.so, no PPPM ticket warranted.

Thanks again for the careful read of the original report — the four-scenario framing was exactly the right pushback.
 
One more update from production deployment today — found a name-form asymmetry the retraction missed.

When I retested yesterday I only verified the mailbox direction. Wiring up the imapsieve_from direction for ham-learning today surfaced this:

Block formNamespace-relative (Spam)Full IMAP path (INBOX.Spam)
mailbox <name>firessilent no-op
imapsieve_from <name>silent no-opfires

All four cells empirically verified with the same trace instrumentation from yesterday. The two block types need opposite name forms to fire.

The only working config on this server (Plesk 18.0.77.2 / Dovecot 2.4.2, Courier-style namespace with prefix = INBOX. and separator = .):

Code:
mailbox Spam {
  sieve_script report-spam {
    type = before
    cause = copy
    path = /etc/dovecot/sieve/report-spam.sieve
  }
}
 
imapsieve_from INBOX.Spam {
  sieve_script report-ham {
    type = before
    cause = copy
    path = /etc/dovecot/sieve/report-ham.sieve
  }
}

Probable cause (observed behavior, not source-confirmed): the two block types appear to resolve mailbox names in different contexts. mailbox resolves through the namespace, so Spam maps correctly to INBOX.Spam on disk. imapsieve_from appears to match against the raw storage path and doesn't apply the namespace prefix. Anyone with the same namespace shape (Plesk default, many Courier-migrated installs) will hit this if they follow the upstream canonical example at Spam Reporting | Dovecot CE verbatim — that example uses imapsieve_from Spam, which silently no-ops on our setup.

Production evidence for the asymmetry test (ham direction, after switching imapsieve_from Spamimapsieve_from INBOX.Spam):

Code:
[2026-04-24T14:45:10+00:00] ENTER script=.../rspamd-learn-ham.sh pid=322426 argv=[[email protected]]
[2026-04-24T14:45:10+00:00] EXIT  pid=322426 rc=0 stdin_bytes=959 rspamc_out=[success = true]

Rspamd controller log, same timestamp:

Code:
rspamd_controller_learn_fin_task: <127.0.0.1> learned message as ham: [email protected]

Counter: Messages learned 3 → 4, BAYES_HAM learned 1 → 2.

Production is now running the asymmetric form with a prominent in-file comment explaining why the two blocks use different name forms. Bayes auto-training is live across our mailboxes.

Leaving the thread open in case Plesk or Dovecot folks want to weigh in on whether the imapsieve_from namespace handling is intentional or a gap worth addressing upstream.
 
Back
Top