Shore Up
a row of physical mailboxes on posts, each with a small fuel-gauge on its door showing how full it is, and a clerk walking the row writing the nearly-full ones onto a clipboard
MailLinux

Automate Dovecot Mailbox Quota Reports with a Shell Script

Ketan Aagja9 min read
No ratings yet

Before you run this

This guide builds a small shell script that runs doveadm quota get for every mailbox on a Dovecot server, writes a plain-text usage report to a file, and emails you a summary that flags anyone at or above a threshold (90% by default). It is a read-only reporting script. It queries quota figures that Dovecot already tracks; it does not create, resize, recalculate, or delete anything in a mailbox.

A few things to be honest about up front:

  • It needs root. doveadm reads Dovecot's configuration and every user's quota through the master process, so run it as root (or via sudo). An unprivileged user will get permission errors or an empty list.
  • The only things that persist are harmless: a report file under a log directory you choose, and a cron entry. Neither touches mail data.
  • Test it first. Read the script before you run it. Run it by hand on one server and eyeball the output — column order and units in doveadm output can differ between Dovecot versions, so confirm the report matches reality before you trust a nightly email. If you want to be extra careful, test the parsing against a single user with doveadm quota get -u testuser@example.com first.
  • The only Dovecot command in this guide that changes state is doveadm quota recalc, which I mention at the end but do not put in the script. Don't add it to a scheduled job casually.

Assumptions for this guide: Debian 12 or Ubuntu 22.04, Dovecot installed from the distribution packages with the quota plugin already configured and working, bash, and a working local MTA (Postfix) plus the mail command from the mailutils package for delivery. On RHEL/AlmaLinux the logic is identical; the mail-sending package is mailx and paths may differ.

Confirm the building block works

Before scripting anything, prove the underlying command works on your box:

sudo doveadm quota get -u someuser@example.com

You should get a small table: Quota name, Type, Value, Limit, %. The STORAGE row is the one we care about. If this errors out, the quota plugin isn't configured and the script has nothing to report — fix that first (see the doveadm-quota(1) man page and the quota configuration section of the Dovecot documentation at doc.dovecot.org).

Now the whole-server version. The -A flag iterates every user in the userdb, and -f tab gives clean tab-separated output that's easy to parse:

sudo doveadm -f tab quota get -A

With -A, Dovecot prepends a Username column, so the fields are: Username, Quota name, Type, Value, Limit, %. Run it once and confirm that order on your version — if it differs, adjust the field numbers in the script below.

The script

Save this as /usr/local/sbin/dovecot-quota-report.sh and make it executable. Replace the three obvious placeholders at the top: the report directory, the threshold, and the recipient address.

#!/usr/bin/env bash
set -euo pipefail

# ---- settings you replace ----
REPORT_DIR="/var/log/dovecot-quota"   # where reports are written; change to taste
THRESHOLD=90                          # flag mailboxes at/above this % of STORAGE quota
MAILTO="admin@example.com"            # who receives the summary; replace
# ------------------------------

HOST_FQDN="$(hostname -f)"
TODAY="$(date +%Y-%m-%d)"
REPORT_FILE="${REPORT_DIR}/quota-${TODAY}.txt"

mkdir -p "$REPORT_DIR"

# Collect quota for all users, tab-separated so it's safe to parse.
raw="$(doveadm -f tab quota get -A)"

# Build the full report file. NR==1 skips the header row from doveadm.
{
  echo "Dovecot mailbox quota report"
  echo "Host: ${HOST_FQDN}"
  echo "Date: ${TODAY}"
  echo
  printf "%-40s %12s %12s %6s\n" "User" "Used" "Limit" "Use%"
  printf '%s\n' "$raw" | awk -F'\t' '
    NR==1 { next }
    $3 == "STORAGE" { printf "%-40s %12s %12s %6s\n", $1, $4, $5, $6 }
  '
} > "$REPORT_FILE"

# Build the over-threshold list. Skip rows with no percentage (unlimited quota).
over="$(printf '%s\n' "$raw" | awk -F'\t' -v t="$THRESHOLD" '
  NR==1 { next }
  $3 == "STORAGE" && $6 != "" && $6 != "-" && ($6 + 0) >= t {
    printf "%s  (%s%%)\n", $1, $6
  }')"

# Only send mail when there is something to flag.
if [ -n "$over" ]; then
  {
    echo "Mailboxes at or above ${THRESHOLD}% of their storage quota on ${HOST_FQDN}:"
    echo
    echo "$over"
    echo
    echo "----- full report -----"
    cat "$REPORT_FILE"
  } | mail -s "Dovecot quota report - ${HOST_FQDN} - ${TODAY}" "$MAILTO"
fi

echo "Report written to ${REPORT_FILE}"

Set permissions:

sudo chown root:root /usr/local/sbin/dovecot-quota-report.sh
sudo chmod 750 /usr/local/sbin/dovecot-quota-report.sh

A few notes on the non-obvious parts:

  • set -euo pipefail makes the script stop on errors and on unset variables rather than sending you a half-built report.
  • The awk uses $3 == "STORAGE" so message-count quota rows don't clutter the report. If you also want to report on the MESSAGE type, add a second block — don't overload one line.
  • ($6 + 0) >= t forces awk to treat the percentage as a number. Rows with an unlimited limit (percentage blank or -) are skipped so they don't trigger false alarms.
  • The units in the Used/Limit columns are whatever doveadm reports (typically kibibytes for storage). I label them plainly rather than converting, so nothing is silently wrong. Convert in awk later if you like, once you've confirmed the unit.

Run it once by hand

Do this before scheduling anything:

sudo /usr/local/sbin/dovecot-quota-report.sh

Check that /var/log/dovecot-quota/quota-YYYY-MM-DD.txt exists and looks right, and that if any mailbox is over the threshold you actually received the email. If you want to force an email during testing, temporarily lower THRESHOLD to something like 1.

Schedule it

Use a cron.d file so the schedule lives in one obvious place. Create /etc/cron.d/dovecot-quota-report:

# Dovecot quota report — runs 07:00 daily as root
0 7 * * * root /usr/local/sbin/dovecot-quota-report.sh >/dev/null 2>&1

Cron mails root any output on error; the >/dev/null 2>&1 keeps normal runs quiet while still letting a crash surface through cron's own mail. Adjust the time and frequency to taste — weekly (0 7 * * 1) is plenty for most servers.

Verify it worked

  • The command: sudo doveadm -f tab quota get -A | head should show real data.
  • The report: ls -l /var/log/dovecot-quota/ and open the latest file. Spot-check one user against doveadm quota get -u thatuser@example.com — the numbers must match.
  • The schedule: sudo systemctl status cron (Debian/Ubuntu; it's crond on RHEL) confirms cron is running, and after the first scheduled run a fresh dated file should appear.

Undo

Nothing here modifies mailboxes, so rollback is just removing what you added:

sudo rm /etc/cron.d/dovecot-quota-report          # stop the schedule
sudo rm /usr/local/sbin/dovecot-quota-report.sh   # remove the script
sudo rm -r /var/log/dovecot-quota                  # remove accumulated reports

If a user's reported usage looks obviously wrong (say, after moving mail on disk), Dovecot can recompute it with doveadm quota recalc -u user@example.com. That changes stored quota state, so run it deliberately per user and never bury it in this reporting script. See the doveadm-quota(1) man page for its exact behaviour before you use it.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.