# Lazy Expunge¶

The lazy expunge plugin provides a “second-chance” to recover messages that would otherwise be deleted from a mailbox by user action.

It does this by moving the message to a defined location (either a mailbox, or a namespace – see below for further details) when a user deletes the message from a mailbox.

This behavior is useful for a variety of reasons:

1. Protect against misconfigured clients (e.g. POP3 client that deletes all messages)

2. Protect against accidental deletion (user error)

3. Archiving

Generally, lazy-expunge is configured so that the expunged mails are not counted in the user’s quota. Unless being used for archiving, autoexpunge should be used to prune the mailbox to control storage usage.

## Configuration¶

There are two plugin configuration options:

lazy_expunge

The mailbox/namespace to move messages to when expunged

lazy_expunge_only_last_instance

If true, only move to expunged storage if this is the last copy of the message in the user’s account

### Storage Locations¶

#### mailbox¶

New in version v2.2.24.

Messages that are expunged are moved to a single mailbox.

This is the simplest configuration. The mailbox is created automatically.

You probably also want to hide it with an ACL from the user, if recovery is only expected to be an action performed by an admin/operator.

To move to a mailbox, do NOT add a trailing delimiter to the lazy_expunge argument.

Example configuration:

namespace inbox {
mailbox .EXPUNGED {
autoexpunge = 7days
autoexpunge_max_mails = 100000
}
}

mail_plugins = $mail_plugins lazy_expunge acl plugin { # Move messages to an .EXPUNGED mailbox lazy_expunge = .EXPUNGED # Define ACL so that user cannot list the .EXPUNGED mailbox acl = vfile:/etc/dovecot/dovecot.acl # Expunged messages most likely don't want to be included in quota: quota_rule = .EXPUNGED:ignore }  Where /etc/dovecot/dovecot.acl contains: .EXPUNGED owner rwstipekxa  You could also leave the permissions empty if you don’t want to allow clients to access it at all. #### namespace¶ Deprecated since version v2.3.0. Expunged messages are moved to mailbox(es) within a defined namespace When a message is expunged from mailbox <name>, it’s moved to a mailbox <name> in the expunge namespace. When an entire mailbox <name> is deleted, it’s moved to this namespace as <name>. If the mailbox already exists in the expunge namespace, the contents are merged. To move to a namespace, you MUST add a trailing delimiter to the lazy_expunge argument. Example: if the namespace delimiter is /, and you want to move to the .EXPUNGED namespace, then the lazy_expunge option should be set to .EXPUNGED/. Example configuration: # Default namespace namespace { prefix = separator = / inbox = yes } # Namespace for lazy_expunge plugin namespace { prefix = .EXPUNGED/ hidden = yes list = no separator = / location = maildir:~/Maildir/expunged } mail_plugins =$mail_plugins lazy_expunge
plugin {
# Move expunged messages into the .EXPUNGED namespace
lazy_expunge = .EXPUNGED/
}

##### mdbox¶

With mdbox, use different MAILBOXDIRs (so copying between namespaces works quickly within the same storage), but otherwise exactly the same paths (INDEX, control):

# Default namespace
namespace {
prefix =
inbox = yes
location = mdbox:~/mdbox:INDEX=/var/index/%d/%n
separator = /
}

# lazy_expunge namespace(s)
namespace {
prefix = .EXPUNGED/
hidden = yes
list = no
separator = /
subscriptions = no

location = mdbox:~/mdbox:INDEX=/var/index/%d/%n:MAILBOXDIR=expunged

# If mailbox_list_index=yes is enabled, it needs a separate index file
# (v2.2.28+):
#location = mdbox:~/mdbox:INDEX=/var/index/%d/%n:MAILBOXDIR=expunged:LISTINDEX=expunged.list.index
}


### Copy only the last instance¶

If a mail has multiple copies within a user account, each copy is normally moved to the lazy expunge storage when it’s expunged.

Example: this may happen when moving a message to Trash, as clients can issue IMAP COPY command to copy the message to Trash before expunging the message from the original mailbox. Deleting later from Trash would result in two copies of the same message in the lazy expunge storage.

With v2.2+ you can set lazy_expunge_only_last_instance = yes to copy only the last instance to the expunge storage. This ensures that only a single copy of a message will appear in the expunge storage.

This setting works with the following mailbox formats:

• Maildir (with maildir_copy_with_hardlinks = yes, which is the default)

• sdbox

• mdbox

• obox with fs-dictmap

### Quota¶

Generally, it is desired that messages in expunge storage are NOT counted towards user quota, as the messages seen by the user will not match-up with the size of the quota otherwise (especially if expunge storage is hidden from users via ACL).

Example to exclude expunge storage from the quota:

plugin {
quota = count:User quota
quota_rule = *:storage=1GB
# Exclude .EXPUNGED mailbox from the quota
quota_rule2 = .EXPUNGED:ignore
}


See Quota.

## Cleaning up¶

Doveadm can be used to manually clean expunge storage.

Example to delete all messages in .EXPUNGED namespace older than one day:

doveadm expunge mailbox '.EXPUNGED/*' savedsince 1d


### autoexpunge¶

Set autoexpunge configuration for expunge storage to automatically clean old messages.

See Namespaces.

## Obox Settings¶

Lazy expunge allows reduction of Cassandra dictmap lookups by removing the lockdir setting and enabling the obox_track_copy_flags setting.

 mail_plugins = \$mail_plugins lazy_expunge
plugin {
lazy_expunge = .EXPUNGED
# If Cassandra w/obox is used:
obox_track_copy_flags = yes
}


# Dumpster¶

See Dumpster Config for information on how to configure lazy_expunge with the OX Dumpster module.