Search K
Appearance
Appearance
Quota tracking and enforcing plugin.
Three plugins are associated with quota:
Name | Description |
---|---|
imap-quota plugin | Enables IMAP commands for requesting and administering current quota. |
quota (this plugin) | Implements the actual quota handling and includes all quota backends. |
quota-clone plugin | Copy the current quota usage to a dict. |
quota
Default | [None] |
---|---|
Value | string |
See Also |
Quota root configuration has the following syntax:
quota = <backend>[:<quota root name>[:<backend args>]]
The quota root name is just an arbitrary string that is sent to IMAP clients, which in turn may show it to the user. The name has no meaning. By default, an empty string is used, but you may want to change that since some clients (Apple Mail) break and don't show quota at all then.
You can define multiple quota roots by appending an increasing number to the setting label:
plugin {
quota = maildir:User quota
quota2 = fs:Disk quota
#quota3 = ...
}
Globally available arguments for <backend args>
parameter:
Name | Description |
---|---|
noenforcing |
Don't try to enforce quotas by calculating if saving would get user over quota. Only handle write failures. |
ns=<prefix> |
A separate namespace-specific quota that's shared between all users. |
If you want to specify multiple backend arguments, separate them with ':'
(e.g. noenforcing:ns=Public/:foo:bar
).
quota_exceeded_message
Default | [None] |
---|---|
Value | string |
The message specified here is passed on to a user who goes over quota.
The value is either the message or the path to a file (prefixed with a
<
) that will be used as the message data.
plugin {
quota_exceeded_message = Quota exceeded.
# Read message from a file
#quota_exceeded_message = </path/to/quota_exceeded_message.txt
}
quota_grace
Default | 10%% |
---|---|
Value | stringsize |
See Also |
If set, allows message deliveries to exceed quota by this value.
quota_mailbox_count
Default | 0 |
---|---|
Value | unsigned integer |
See Also | |
Changes |
|
Maximum number of mailboxes that can be created. Each namespace is tracked separately, so e.g. shared mailboxes aren't counted towards the user's own limit.
0
means unlimited.
quota_mailbox_message_count
Default | 0 |
---|---|
Value | unsigned integer |
Changes |
|
Maximum number of messages that can be created in a single mailbox.
0
means unlimited.
quota_max_mail_size
Default | 0 |
---|---|
Value | unsigned integer |
See Also |
The maximum message size that is allowed to be saved (e.g. by LMTP, IMAP APPEND or doveadm save).
0
means unlimited.
quota_over_flag
Default | 10%% |
---|---|
Value | string |
See Also |
An identifier that indicates whether the overquota-flag is active for a user.
This identifier is compared against quota_over_flag_value
to
determine if the overquota-flag should be determine to be set for the user.
Usually, this value will be loaded via userdb.
quota_over_flag_lazy_check
Default | no |
---|---|
Value | boolean |
If enabled, overquota-flag is checked only when current quota usage is going to already be checked.
Can be used to optimize the overquota-flag check in case it is running too slowly.
quota_over_flag_value
Default | [None] |
---|---|
Value | string |
See Also |
The search string to match against quota_over_flag
to
determine if the overquota-flag is set for the user.
Wildcards can be used in a generic way, e.g. *yes
or *TRUE*
.
quota_over_script
Default | [None] |
---|---|
Value | string |
The service script to execute if overquota-flag is wrong. Configured the
same as quota_warning
scripts.
The current quota_over_flag
value is appended as the last
parameter.
plugin {
quota_over_script = quota-warning mismatch %u
}
quota_rule
Default | [None] |
---|---|
Value | string |
See Also |
Quota rule configuration has the following syntax:
quota_rule = <mailbox name>:<limit configuration>
You can define multiple quota rules by appending an increasing number to the setting label.
*
as the mailbox name configures the default limit, which is applied on
top of a mailbox-specific limit if found.
?
as the mailbox name works almost like *
. The difference is that
?
is used only if quota backend doesn't override the limit.
*
and ?
wildcards can be used as a generic wildcard in mailbox
names, so for example box*
matches boxes
.
The following limit names are supported:
Name | Description |
---|---|
backend |
Quota backend-specific limit configuration. |
bytes |
Quota limit (without suffix: in bytes). 0 means unlimited. |
ignore |
Don't include the specified mailbox in quota at all. |
messages |
Quota limit in number of messages. 0 means unlimited. |
storage |
Quota limit (without suffix: in kilobytes). 0 means unlimited. |
Settings with a limit value support the size syntax as a suffix.
Settings also support %
as a suffix. Percents are relative to the
default rule. For example:
plugin {
quota = maildir:User quota
quota_rule = *:storage=1GB
# 10% of 1GB = 100MB
quota_rule2 = Trash:storage=+10%%
# 20% of 1GB = 200MB
quota_rule3 = Spam:storage=+20%%
}
Note that %
is written twice to escape it, because Config variables
are
expanded in plugin section.
userdb configuration may or may not require this escaping.
Backend-specific configuration currently is used only with Maildir++
quota backend. It means you can have the quota in Maildir++ format (e.g.
10000000S
).
quota_warning
Default | [None] |
---|---|
Value | string |
See Also |
You can configure Dovecot to run an external command when user's quota exceeds a specified limit. Note that the warning is ONLY executed at the exact time when the limit is being crossed, so when you're testing you have to do it by crossing the limit by saving a new mail. If something else besides Dovecot updates quota so that the limit is crossed, the warning is never executed.
Quota warning configuration has the following syntax:
quota_warning = <limit configuration> <quota-warning socket name> <parameters>
limit_configuration
is almost exactly same as for
quota
, with the exception of adding -
before
the value for "reverse" warnings where the script is called when quota
drops below the value. Usually you want to use percents instead of absolute
limits.
Only the command for the first exceeded limit is executed, so configure the
highest limit first. The actual commands that are run need to be created as
services (create a named Dovecot service and use the service name
as the quota-warning socket name
argument).
Note: The percent sign (%
) needs to be written as %%
to avoid
config expansion (see Config variables
).
You can define multiple quota rules by appending an increasing number to the setting label.
Enable in configuration files, e.g.:
# Enable quota plugin for tracking and enforcing the quota.
mail_plugins = $mail_plugins quota
protocol imap {
# Enable the IMAP QUOTA extension, allowing IMAP clients to ask for the
# current quota usage.
mail_plugins = $mail_plugins imap_quota
}
plugin {
quota_grace = 10%%
# 10% is the default
quota_status_success = DUNNO
quota_status_nouser = DUNNO
quota_status_overquota = "552 5.2.2 Mailbox is full"
}
See quota
for details on the syntax of the quota root setting.
Quota root is a concept from IMAP Quota specifications (RFC 2087). Normally you'll have only one quota root, but in theory there could be, e.g., "user quota" and "domain quota" roots. It's unspecified how the quota roots interact with each other (if at all).
In some systems, for example, INBOX could have a completely different quota root from the rest of the mailboxes (e.g. INBOX in /var/mail/
partition and others in /home/
partition).
See quota_rule
for details on the syntax of the quota rule setting.
For Maildir++ quota, if maildirsize
file exists the limits are taken from it but if it doesn't exist the ?
limits are used.
quota_rule = *:storage=1G
quota_rule2 = Trash:storage=+100M
quota_rule3 = SPAM:ignore
This means that the user has 1GB quota, but when saving messages to Trash mailbox it's possible to use up to 1.1GB of quota. The quota isn't specifically assigned to Trash, so if you had 1GB of mails in Trash you could still save 100MB of mails to Trash, but nothing to other mailboxes. The idea of this is mostly to allow the clients' move-to-Trash feature work while user is deleting messages to get under quota.
Additionally, any messages in the SPAM folder are ignored per the ignore
directive and would not count against the quota.
The first quota rule muse be named quota_rule
while the following rules have an increasing digit in them. You can have as many quota rules as you want.
You can override quota rules in your userdb extra fields. Keep global settings in configuration plugin section and override only those settings you need to in your userdb.
If you're wondering why per-user quota isn't working:
-d
parameter.quota_rule
setting is properly returned by userdb. Enable log_debug = category=auth or category=mail
to see this.Note
Remember that user_attrs
is used only if you use LDAP authentication.
Quota limit is in quotaBytes
field:
user_attrs = homeDirectory=home, quotaBytes=quota_rule=*:bytes=%$
Note
user_query
is used only if you use SQL authentication database.
Example (for MySQL):
user_query = SELECT uid, gid, home, \
concat('*:bytes=', quota_limit_bytes) AS quota_rule \
FROM users WHERE userid = '%u'
# MySQL with userdb prefetch: Remember to prefix quota_rule with userdb_
# (just like all other userdb extra fields):
password_query = SELECT userid AS user, password, \
uid AS userdb_uid, gid AS userdb_gid, \
concat('*:bytes=', quota_limit_bytes) AS userdb_quota_rule \
FROM users WHERE userid = '%u'
Example (for PostgreSQL and SQLite):
user_query = SELECT uid, gid, home, \
'*:bytes=' || quota_limit_bytes AS quota_rule \
FROM users WHERE userid = '%u'
Example passwd-file authentication database entries:
user:{plain}pass:1000:1000::/home/user::userdb_quota_rule=*:bytes=100M
user2:{plain}pass2:1001:1001::/home/user2::userdb_quota_rule=*:bytes=200M
user3:{plain}pass3:1002:1002::/home/user3::userdb_mail=maildir:~/Maildir userdb_quota_rule=*:bytes=300M
The passwd authentication database userdb doesn't support extra fields. That's why you can't directly set users' quota limits to passwd file. One possibility would be to write a script that reads quota limits from another file, merges them with passwd file and produces another passwd-file, which you could then use with Dovecot's passwd-file authentication database.
You can create a separate namespace-specific quota that's shared between all users. This is done by adding :ns=<namespace prefix>
parameter to quota setting. For example:
namespace {
type = public
prefix = Public/
#location = ..
}
plugin {
quota = maildir:User quota
quota2 = maildir:Shared quota:ns=Public/
#quota_rules and quota2_rules..
}
You can create a separate namespace-specific quota for a folder hierarchy. This is done by adding another namespace and the :ns=<namespace prefix>
parameter to quota setting. For example:
namespace {
type = private
prefix = Archive/
#location = ..
}
plugin {
# Maildir quota
quota = maildir:User quota:ns=
quota2 = maildir:Archive quota:ns=Archive/
# Count quota
#quota = count:User quota:%u.default:ns=
#quota2 = count:Archive quota:%u.archive:ns=Archive/
#quota_rules and quota2_rules..
}
Note
If you're using dict quota, you need to make sure that the quota of the Archive
namespace is calculated for another "user" than the default namespace. Either track different namespaces in different backends or make sure the users differs.
%u.archive
defines <username>.archive
as key to track quota for the Archive
namespace; %u.default
tracks the quota of other folders.
See Config variables
for further help on variables.
Quota plugin considers shared namespaces against owner's quota, not the current user's.
There is a limitation that per-user quota configuration is ignored, and the current user's configuration is used.
Public namespaces are ignored unless there is explicit quota specified for it.
Example:
plugin {
quota_exceeded_message = Quota exceeded, please go to http://www.example.com/over_quota_help for instructions on how to fix this.
}
Quota backend specifies the method how Dovecot keeps track of the current quota usage. They don't (usually) specify users' quota limits, that's done by returning extra fields from userdb.
We recommend using count
for any new installations.
If you need usage data to an external database, consider using quota-clone plugin for exporting the information. (It's very slow to query every user's quota from the index files directly.)
The count
quota backend tracks the quota internally within Dovecot's index files.
Recommended
This is the RECOMMENDED way of calculating quota on recent Dovecot installations.
Each mailbox's quota is tracked separately and when the current quota usage is wanted to be known, the mailboxes' quotas are summed up together. To get the best performance, mailbox list indexes should be enabled.
WARNING
If you're switching from some other quota backend to count
, make sure that all the mails have their virtual sizes already indexed. Otherwise there may be a significant performance hit when Dovecot starts opening all the mails to get their sizes. You can help to avoid this by accessing the mailbox vsizes for all the users before doing the configuration change: doveadm mailbox status -u user@domain vsize '\*'
.
count
backend doesn't have any additional parameters.
mailbox_list_index = yes
# Avoid spending excessive time waiting for the quota calculation to finish
# when mails' vsizes aren't already cached. If this many mails are opened,
# finish the quota calculation on background in indexer-worker process. Mail
# deliveries will be assumed to succeed, and explicit quota lookups will
# return internal error.
protocol !indexer-worker {
mail_vsize_bg_after_count = 100
}
plugin {
# 10MB quota limit
quota = count:User quota
quota_rule = *:storage=10M
}
The fs
(filesystem) quota backend supports both local filesystems and rquota (NFS).
By default only user quota is shown, or if it doesn't exist, group quota is used as fallback.
Driver specific parameters:
Name | Description |
---|---|
group | Report only group quotas |
inode_per_mail | Report inode quota as "number of message" quota. This can be useful with Maildir or single-dbox. |
mount=<path> | Report quota from given path. Default is to use the path for the mail root directory. |
user | Report only user quotas, don't fallback to showing group quotas. |
If you are using systemd, please make sure you turn offPrivateDevices=yes
, otherwise the backend won't work properly. The best way to do this is to use systemctl edit dovecot
command or add file /etc/systemd/system/dovecot.service.d/override.conf
with:
[Service]
PrivateDevices=off
It's a good idea to keep index files in a partition where there are no filesystem quota limits. The index files exist to speed up mailbox operations, so Dovecot runs more slowly if it can't keep them updated. You can specify the index file location by appending :INDEX=/somewhere
to mail_location
.
Dovecot can handle "out of disk space" errors in index file handling and transparently move to in-memory indexes. It'll use the in-memory indexes until the mailbox is re-opened.
It's a good idea to have mbox_lazy_writes = yes
(default), otherwise Dovecot might give "Not enough disk space" errors when opening the mailbox, making it impossible to expunge any mails.
If user has run out of quota and index files are also in memory (because they're also over quota), it's possible that message flag changes are lost. This should be pretty rare though because Dovecot keeps some extra space allocated inside the mbox file for flag changes.
Example:
mail_location = mbox:~/mail:INBOX=/var/mail/%u:INDEX=/var/no-quotas/index/%u
Maildir needs to be able to add UIDs of new messages to dovecot-uidlist
file. If it can't do this, it can give an error when opening the mailbox, making it impossible to expunge any mails.
Currently the only way to avoid this is to use a separate partition for the uidlist files where there are no filesystem quota limits. You can do this by appending :CONTROL=/somewhere
to mail_location
.
Example:
mail_location = maildir:~/Maildir:INDEX=/var/no-quotas/index/%u:CONTROL=/var/no-quotas/control/%u
Note that if you change the location of the control files, Dovecot will look in the new CONTROL
directory (/var/no-quotas/control/%u
) for the subscriptions file.
mail_plugins = $mail_plugins quota
protocol imap {
mail_plugins = mail_plugins imap_quota
}
plugin {
quota = fs:user
}
If you want to see both user and group quotas as separate quota roots, you can use:
plugin {
quota = fs:User quota:user
quota2 = fs:Group quota:group
}
If you have your mails in two filesystems, you can create two quota roots:
plugin {
# Assuming INBOX in /var/mail/ which is mounted to /
quota = fs:INBOX:mount=/
# Assuming other mailboxes are in /home mount
quota2 = fs:Others:mount=/home
}
See imapc.
Maildir++ is the most commonly used quota backend with Maildir format.
WARNING
Note that Maildir++ quota works only with Maildir format. With other mailbox formats you should use count
.
The maildir
quota backend implements Maildir++ quota in Dovecot. Dovecot implements the Maildir++ specification so Dovecot remains compatible with Courier, maildrop, Exim, etc.
There are two ways to configure Maildir++ quota limits:
maildirsize
files.Only Maildir++-specific settings are described below.
Maildir++ quota relies on maildirsize
file having correct information, so if your users can modify the file in some way (e.g. shell access), you're relying on the goodwill of your users for the quota to work.
You can't rely on Dovecot noticing external changes to Maildir and updating maildirsize accordingly. This happens eventually when quota is being recalculated, but it may take a while. Quota recalculation also currently doesn't trigger quota warning executions.
The maildirsize
file in the Maildir root directory contains both the quota limit information and the current quota status. It contains a header in format:
<storage limit in bytes>S,<messages limit>C
If you don't configure any quota limits in Dovecot (quota=maildir
with no other settings), Dovecot takes the limits from the header. If the file does not exist, quota isn't enforced.
If you configure quota limits in Dovecot, Dovecot makes sure that this header is kept up to date. If the file does not exist, it's simply rebuilt.
Once the maildirsize
reaches 5120 bytes, the quota is recalculated and the file is recreated. This makes sure that if quota happens to be broken (e.g. externally deleted files) it won't stay that way forever.
The quota service allows postfix to check quota before delivery:
service quota-status {
executable = quota-status -p postfix
inet_listener {
# You can choose any port you want
port = 12340
}
client_limit = 1
}
And then have postfix check_policy_service
check that:
smtpd_recipient_restrictions =
...
check_policy_service inet:mailstore.example.com:12340
For more about this service, see https://sys4.de/en/blog/postfix-dovecot-mailbox-quota/
See quota_warning
.
plugin {
quota_warning = storage=95%% quota-warning 95 %u
quota_warning2 = storage=80%% quota-warning 80 %u
quota_warning3 = -storage=100%% quota-warning below %u # user is no longer over quota
}
service quota-warning {
executable = script /usr/local/bin/quota-warning.sh
# use some unprivileged user for executing the quota warnings
user = vmail
unix_listener quota-warning {
}
}
With the above example, when user's quota exceeds 80% quota-warning.sh
is executed with parameter 80
. The same goes for when quota exceeds 95%. If user suddenly receives a huge mail and the quota jumps from 70% to 99%, only the 95 script is executed.
You have to create the quota-warning.sh
script yourself. Here is an example that sends a mail to the user:
quota-warning.sh
#!/bin/sh
PERCENT=$1
USER=$2
cat << EOF | /usr/local/libexec/dovecot/dovecot-lda -d $USER -o "plugin/quota=maildir:User quota:noenforcing"
From: postmaster@domain.com
Subject: quota warning
Your mailbox is now $PERCENT% full.
EOF
The quota enforcing is disabled to avoid looping. You'll of course need to change the plugin/quota
value to match the quota backend and other configuration you use. Basically preserve your original quota
setting and just insert :noenforcing
to proper location in it.
For example with dict quota, you can use something like: -o "plugin/quota=count:User quota::noenforcing"
.
Quota warning scripts can be used to set an overquota-flag to userdb (e.g. LDAP) when user goes over/under quota. This flag can be used by MTA to reject mails to an user who is over quota already at SMTP RCPT TO stage.
A problem with this approach is there are race conditions that in some rare situations cause the overquota-flag to be set even when user is already under quota. This situation doesn't solve itself without manual admin intervention or the new overquota-flag feature: This feature checks the flag's value every time user logs in (or mail gets delivered or any other email access to user) and compares it to the current actual quota usage. If the flag is wrong, a script is executed that should fix up the situation.
The overquota-flag name in userdb must be quota_over_flag
.
These settings are available:
Example:
plugin {
# If quota_over_flag=TRUE, the overquota-flag is enabled. Otherwise not.
quota_over_flag_value = TRUE
# Any non-empty value for quota_over_flag means user is over quota.
# Wildcards can be used in a generic way, e.g. "*yes" or "*TRUE*"
#quota_over_flag_value = *
quota_over_flag_lazy_check = yes
quota_over_script = quota-warning mismatch %u
}
See quota_grace
.
By default the last mail can bring user over quota. This is useful to allow user to actually unambiguously become over quota instead of fail some of the last larger mails and pass through some smaller mails. Of course the last mail shouldn't be allowed to bring the user hugely over quota, so by default this limit is 10% of the user's quota limit.
To change the quota grace, use:
plugin {
# allow user to become max 10% over quota
quota_grace = 10%%
# allow user to become max 50 MB over quota
quota_grace = 50 M
}
See quota_mailbox_count
.
Maximum number of mailboxes that can be created. Each namespace is tracked separately, so e.g. shared mailboxes aren't counted towards the user's own limit. The default is 0
, which is unlimited.
See quota_mailbox_message_count
.
Maximum number of messages that can be created in a single mailbox.
See quota_max_mail_size
.
Dovecot allows specifying the maximum message size that is allowed to be saved (e.g. by LMTP, IMAP APPEND or doveadm save). The default is 0
, which is unlimited.
Since outgoing mail sizes are also typically limited on the MTA side, it can be beneficial to prevent user from saving too large mails, which would later on fail on the MTA side anyway.
Indicates that the quota plugin should use virtual sizes rather than physical sizes when calculating message sizes. Required for the count
driver.
This is automatically determined by the quota plugin.
The imap-quota plugin implements the SETQUOTA
command, which allows changing the logged in user's quota limit if the user is admin.
See imap-quota plugin for further configuration information.
If your quotas are out of sync, you can use doveadm quota recalc -u <uid>
command to recalculate them.
Standard way to expunge messages with IMAP works by:
\Deleted
flagBoth of these commands can be successfully used while user's quota is full. However many clients use a move-to-Trash
feature, which works by:
clean trash
feature is used)If user is over quota (or just under it), the first COPY command will fail and user may get an unintuitive message about not being able to delete messages because user is over quota. The possible solutions for this are:
To make sure users don't start keeping messages permanently in Trash you can use namespace/mailbox/autoexpunge
to expunge old messages from Trash mailbox.
User's current quota usage can be looked up with doveadm quota get -u user@domain
.
User's current quota may sometimes be wrong for various reasons (typically only after some other problems). The quota can be recalculated with doveadm quota recalc -u user@domain
.