Shore Up
A row of pressure gauges mounted on pipes; one pipe is swelling and its gauge needle is deep in the red while a small alarm bell rings, and an engineer notes it on a clipboard.
LinuxMail

Monitor Dovecot Process Memory and Alert on Runaway Processes

Ketan Aagja8 min read
No ratings yet

Before you run this

This guide sets up a small bash script, run on a schedule, that reads the resident memory (RSS) of every process inside the dovecot.service cgroup and warns you when one crosses a threshold you set. It only reads and reports — it writes a line to syslog and optionally sends an email. It does not kill processes, change Dovecot config, or touch any mailbox.

  • Privileges: the script needs to read /sys/fs/cgroup/system.slice/dovecot.service/cgroup.procs and each process's RSS. Reading another user's process details reliably means running it as root (via a root cron job or a root-owned systemd timer). It makes no changes, so the risk of running it as root is low, but read it before you trust it.
  • Test first: read the script top to bottom, then run it by hand on a non-production mail server or a test VM before you schedule it anywhere real. Confirm it selects the right PIDs and that your threshold is sane for your load.
  • What it changes: nothing persistent beyond the script file and one cron entry. There is nothing destructive and nothing to reverse in Dovecot itself. Undo is simply removing the cron entry and the script (shown at the end).
  • One honest caveat: RSS is a snapshot, not proof of a leak. A genuine leak shows as RSS that climbs and never comes back down across restarts of the same worker. Use this watchdog to catch growth; use Dovecot's own vsz_limit settings (below) to cap it.

Assumed environment: Debian 12 or Ubuntu 22.04, systemd with cgroup v2 (the default on both), Dovecot from the distro package, bash, and mail from the mailutils package if you want email alerts. If you are on an older cgroup-v1 host, the cgroup path differs — check systemctl status dovecot to see the actual cgroup for your system.

First, look at what you have

Before automating anything, see the current picture. The service-level memory is easiest:

# Live per-service memory and CPU; press q to quit.
systemd-cgtop

For per-process RSS inside Dovecot, read the PIDs straight from the service cgroup and ask ps for each. RSS here is in kilobytes:

# List every Dovecot worker with its RSS, largest last.
for pid in $(cat /sys/fs/cgroup/system.slice/dovecot.service/cgroup.procs); do
  ps -o pid=,rss=,comm= -p "$pid"
done | sort -k2 -n

You will see the dovecot master plus workers named imap, pop3, lmtp, imap-login, auth, and so on. Note the typical RSS of a busy imap worker on your box — that number sets your threshold. On a quiet server most workers sit well under 100 MB; a worker at 500 MB and climbing is worth a look.

Using the cgroup rather than pgrep dovecot matters: worker executables are named imap, pop3, etc., so a name match misses them, while the cgroup contains exactly the processes systemd started for the service.

The watchdog script

Save this as /usr/local/sbin/monitor-dovecot-mem.sh and make it executable (chmod 750). Replace admin@example.com with your address, and tune THRESHOLD_KB to your environment.

#!/usr/bin/env bash
# monitor-dovecot-mem.sh — warn when a Dovecot worker exceeds an RSS threshold.
# Read-only: logs to syslog and optionally emails. Does not kill anything.
set -euo pipefail

# --- Configuration ---------------------------------------------------------
THRESHOLD_KB=524288                 # 512 MiB per process, expressed in kB
CGROUP_PROCS="/sys/fs/cgroup/system.slice/dovecot.service/cgroup.procs"
ALERT_EMAIL="admin@example.com"     # your address; set to "" to skip email
HOST="$(hostname -s)"
# ---------------------------------------------------------------------------

if [[ ! -r "$CGROUP_PROCS" ]]; then
    echo "Cannot read $CGROUP_PROCS — is Dovecot running? cgroup v2?" >&2
    exit 1
fi

breaches=""

while read -r pid; do
    [[ -z "$pid" ]] && continue
    # RSS (kB) and command name; skip if the PID vanished mid-scan.
    read -r rss comm < <(ps -o rss=,comm= -p "$pid" 2>/dev/null) || continue
    [[ -z "${rss:-}" ]] && continue
    if (( rss > THRESHOLD_KB )); then
        line="pid=$pid comm=$comm rss_kb=$rss threshold_kb=$THRESHOLD_KB"
        breaches+="$line"$'\n'
        logger -t dovecot-memwatch "OVER THRESHOLD: $line"
    fi
done < "$CGROUP_PROCS"

if [[ -n "$breaches" ]]; then
    if [[ -n "$ALERT_EMAIL" ]] && command -v mail >/dev/null; then
        printf 'Dovecot workers over %s kB RSS on %s:\n\n%s' \
            "$THRESHOLD_KB" "$HOST" "$breaches" \
            | mail -s "[$HOST] Dovecot memory alert" "$ALERT_EMAIL"
    fi
    exit 2   # non-zero so a wrapping timer or monitor notices the breach
fi

exit 0

A few notes on the non-obvious bits: set -euo pipefail makes the script fail loudly rather than limp on with a bad variable; logger -t dovecot-memwatch tags every line so you can filter the journal later; and the exit 2 on breach gives you a non-zero status if you ever wrap this in a monitoring check that cares about exit codes.

Schedule it

The mainstream choice is cron. Create /etc/cron.d/dovecot-memwatch:

# Check Dovecot worker memory every 5 minutes.
*/5 * * * * root /usr/local/sbin/monitor-dovecot-mem.sh

The root field is required in /etc/cron.d files. Cron mails root any stdout the script produces on error, which is a useful backstop separate from the alert email.

A systemd timer is the other common approach and is a fine alternative if you prefer journald over cron mail — the mechanism is a .service unit calling the script plus a .timer with OnCalendar=*:0/5. I'm keeping to cron here so there's one path to follow.

Cap it at the source (the real fix)

Alerting tells you a worker is bloating; it does not stop it. Dovecot's own guard is default_vsz_limit (a virtual-size limit that Dovecot enforces by terminating a worker that exceeds it), with per-service overrides such as a vsz_limit inside a service imap { ... } block. Note that this limits virtual size, not RSS, so it won't line up exactly with the RSS numbers you're alerting on. Check the exact current syntax and defaults in the official Dovecot documentation (the settings reference under doc.dovecot.org) before you change these — I'm deliberately not pasting values here, because the default has changed between releases and a wrong limit will start killing live sessions.

Verify it works

Run it by hand first and confirm a clean exit:

sudo /usr/local/sbin/monitor-dovecot-mem.sh; echo "exit=$?"

To prove the alert path fires without waiting for a real leak, temporarily set THRESHOLD_KB very low (say 1024) so ordinary workers trip it, run the script again, then check the journal:

journalctl -t dovecot-memwatch --since "10 minutes ago"

You should see OVER THRESHOLD lines, and — if you configured ALERT_EMAIL and have mail installed — a message in that inbox. Set THRESHOLD_KB back to a realistic value afterward. Confirm the cron job is registered:

sudo systemctl status cron    # the cron daemon is running
sudo run-parts --report --test /etc/cron.d 2>/dev/null || cat /etc/cron.d/dovecot-memwatch

Undo

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

sudo rm /etc/cron.d/dovecot-memwatch
sudo rm /usr/local/sbin/monitor-dovecot-mem.sh

If you also changed default_vsz_limit or a service vsz_limit, revert that edit in your Dovecot config and reload with sudo systemctl reload dovecot, then confirm with sudo doveconf default_vsz_limit to see the effective value.

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.

Report Mail Users Over Quota with Cron

This is a read-and-report job. It runs doveadm quota get to list every mail user's quota usage, picks out the ones over a percentage you set, and emails you a summary. It does not change quotas, delete mail, or touch mailboxes — nothing here is destructive.

9 min read

Automate Dovecot Mailbox Quota Reports with a Shell Script

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.

9 min read

Alert on a Growing Postfix Mail Queue with a Cron Script

A mail queue that quietly grows is one of those failures you notice late — usually when a user asks why their mail from three hours ago hasn't arrived. A backlog can mean a dead relay host, a DNS problem, a downstream server rejecting everything, or an outbound spam run from a compromised account. This guide sets up a small cron job that counts the queue and emails you when it crosses a threshold, so you hear about it early.

9 min read

Automate a Scheduled Reachability and Speed Test to Key Hosts

This guide builds a small bash script that pings a fixed list of hosts on a schedule, records latency and packet loss to a CSV file, and optionally measures throughput to a host you control. It is read-only from a system-configuration standpoint : it changes no firewall rule, route, or setting. It only sends test traffic and appends lines to a log file.

10 min read