Home / Alt manpages / doveadm-expunge(1)

  • doveadm-expunge(1)
  • User command
  • linux

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.

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 Junk is not automatically the same as Spam.
  • Know which date you mean. before and since use the internal date. sentbefore and sentsince use 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.

  • -u for one user or a mask. For example -u '*@example.org'. Preview that same mask with doveadm search first.
  • -A for 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 SQL iterate_query and LDAP iterate_attrs/iterate_filter. A broken iterator can make an apparently successful batch incomplete.
  • -F USER_FILE for 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 doveadm binary and Dovecot package version.
  • Query is tight. It names the intended mailbox and a clear age, flag or message condition.
  • Preview done. doveadm search or doveadm fetch showed 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.