Delete Dovecot Mail for Good with doveadm expunge
You need to purge old Spam from a Dovecot mailbox, and doveadm expunge deletes it permanently, so preview first. Allow about fifteen minutes for a single-user cleanup, longer for a batch.
The route
Jump straight to the step you need, or tick off Done means at the end.
You will preview a search, confirm its scope, then remove the matches from one mailbox or a deliberately chosen set of users. You need shell access to the mail host, Dovecot's doveadm command, and enough privilege to run mail administration commands. Use a maintenance window if the mailbox is busy.
Warning
Expunge is destructive. It removes messages rather than moving them to Trash, and this command has no undo operation. Take a backup or snapshot that you have actually tested before acting on mail you may need to recover.
1. Check the installed command
Confirm the binary and package version first. These are read-only checks:
$ command -v doveadm
/usr/bin/doveadm
$ dpkg-query -W -f='${Package} ${Version}\n' dovecot-core
dovecot-core 1:2.3.21+dfsg1-2ubuntu6.5
$ doveadm help expunge
Your package version will differ. The installed manpage is labelled Dovecot v2.3 and documents -d for deleting an empty mailbox after expunging. On this host, the installed binary's help uses -m in the expunge synopsis instead. Treat the command's own help and a controlled test as authoritative for the exact binary you will run. Do not copy an option from one version into another without checking it.
Checkpoint
You know which doveadm is in your path, which package supplied it, and whether the local binary and manpage agree about optional flags.
2. Build the narrowest possible query
A search query is made from keys such as mailbox, savedbefore, before, subject and all. Multiple expressions are combined with AND by default. The mailbox name is not a shell option. It is part of the query and must be followed by its value.
For example, this targets messages saved more than two weeks ago in one user's Spam mailbox:
mailbox Spam savedbefore 2w
- Spell the mailbox exactly. Use the spelling and namespace your deployment uses. A folder displayed as
Junkis not automatically the same asSpam. - Know which date you mean.
beforeandsinceuse the internal date.sentbeforeandsentsinceuse the Date header. - Start conservative. Do not begin with
ALL, a wildcard user mask, or a query that omits the mailbox unless you have inspected the match set and want that scope.
3. Preview matches without changing mail
Run doveadm search with the same user and query. This is the key safety checkpoint: it lists matching mailbox GUID and message UID pairs without expunging them.
$ doveadm search -u '[email protected]' mailbox Spam savedbefore 2w
8aad0f0a30169f4bea620000ca356bad 18751
8aad0f0a30169f4bea620000ca356bad 18756
The output is host-specific. An empty result means nothing matches that exact query for that user. Do not treat it as permission to broaden the query casually. If you need more context, fetch selected fields for the same query:
$ doveadm fetch -u '[email protected]' 'mailbox.guid uid flags internaldate' mailbox Spam savedbefore 2w
Checkpoint
Compare the preview with the intended account, mailbox, age boundary and approximate message count. If the result is surprising, stop and inspect the query before proceeding.
4. Expunge one user's matches
Only after the preview is correct, run the matching expunge as an administrator or as the authorised Dovecot system user:
# doveadm expunge -u '[email protected]' mailbox Spam savedbefore 2w
There may be no success output. Check the exit status immediately:
# printf 'exit status: %s\n' "$?"
exit status: 0
# doveadm search -u '[email protected]' mailbox Spam savedbefore 2w
A status of zero means the command completed. It does not make the messages recoverable. The final search should return no rows for the same query, unless new matching mail arrived during the operation. Re-run the preview if you need to tell those cases apart.
5. Understand the scope switches
Without -u, -A or -F, the manpage says the command uses the environment of the currently logged-in system user. That is easy to misread as "all users". It is not.
-ufor one user or a mask. For example-u '*@example.org'. Preview that same mask withdoveadm searchfirst.-Afor every user. It applies the query to all users returned by the user database, so it depends on userdb iteration settings. The manpage warns about system users with the passwd userdb and calls out SQLiterate_queryand LDAPiterate_attrs/iterate_filter. A broken iterator can make an apparently successful batch incomplete.-F USER_FILEfor a reviewed list. It reads one username per line and is easier to review than a broad wildcard.
For -F, create the file outside the mail store, inspect it, and remove it after the run if it contains sensitive account names:
$ sed -n '1,20p' /tmp/dovecot-users.txt
[email protected]
[email protected]
$ sudo doveadm expunge -F /tmp/dovecot-users.txt mailbox Spam savedbefore 2w
Tip
Do not combine broad scope with a first attempt. Preview each user where practical, or run a small canary file and compare its results before expanding the list.
6. Avoid accidental mailbox deletion
The supplied v2.3 manpage documents -d as deleting a mailbox when it is empty after expunging. That is a separate, more disruptive state change. Leave it out unless deleting the mailbox itself is part of the approved operation. This installed binary advertises a different short option in its help, so verify the local syntax before using any empty-mailbox option.
Warning
If you need to delete an empty mailbox, record its name, confirm it is not a shared or system mailbox, and check your backup or mailbox recreation procedure first. Expunging messages and deleting the mailbox are different actions, and a successful expunge gives you no rollback for either.
Done means
- Command confirmed. You checked the installed
doveadmbinary and Dovecot package version. - Query is tight. It names the intended mailbox and a clear age, flag or message condition.
- Preview done.
doveadm searchordoveadm fetchshowed the matches before any deletion. - Scope chosen. You picked one user, a reviewed file, or an intentionally configured all-user iteration.
- Result verified. The expunge returned status zero and a repeat search shows the expected result.
- Mailbox kept. You did not use an empty-mailbox deletion option without separate approval and a recovery plan.