PowerMTA High Memory Usage Diagnosis: Complete 2026 Operator Guide

← PowerMTA Operations

PowerMTA High Memory Usage Diagnosis: Complete 2026 Operator Guide

January 9, 2027·12 min read·Henrik Larsen

Why memory diagnosis matters

Memory pressure is one of the failure modes that can take PowerMTA from working fine to not working at all, and the transition can be abrupt. A PowerMTA that uses memory steadily within its allocation runs reliably; a PowerMTA that exhausts physical RAM starts swapping and delivers slowly and unpredictably; a PowerMTA that triggers the Linux OOM killer can be killed outright, taking the service down. Diagnosing memory usage correctly means distinguishing the normal, healthy high memory usage that PowerMTA legitimately needs from the genuine memory problems that require action.

This guide exists because PowerMTA memory usage is widely misunderstood. Operators see PowerMTA using a large fraction of available RAM and assume something is wrong, when in many cases the usage is entirely normal and proportionate to the workload. Conversely, operators sometimes miss a genuine memory problem because they are not watching the signals that distinguish a problem from normal operation. The structure: what PowerMTA actually holds in memory, the distinction between normal growth and a genuine problem, reading memory metrics with standard tools, the relationship between queue depth and memory, the common causes of memory pressure, swap activity as the key danger signal, the OOM killer risk, the choice between adding RAM and fixing configuration, and the systematic diagnostic procedure.

What PowerMTA holds in memory

PowerMTA holds several things in memory, each for a legitimate purpose:

ComponentWhat it isScales with
Message queue metadataInformation about every queued messageQueue depth
Accounting buffersRecords waiting to flush to the accounting logLogging rate vs flush rate
DNS resolution cacheCached MX and A records for destinationsNumber of distinct destinations
Connection stateData structures for active SMTP connectionsConcurrent connection count
Process working memoryGeneral working memory of the processRoughly constant

The largest variable component is usually message queue metadata. PowerMTA holds metadata for every queued message so it can manage retries, throttling, and delivery. A PowerMTA with a deep queue holds metadata for every queued message, so memory usage rises and falls with queue depth. This is by design, and it is one of the reasons memory matters more than CPU for PowerMTA sizing at scale.

The accounting buffers hold records that have been generated but not yet flushed to the accounting log. Under normal operation these stay small because PowerMTA flushes regularly; they grow only if the accounting write path is slow or blocked.

The DNS cache holds resolved MX and A records to avoid repeating DNS lookups for the same destinations. It grows with the number of distinct destination domains but is rarely the dominant memory component.

Connection state scales with concurrent connections; a PowerMTA running thousands of concurrent SMTP connections holds the corresponding data structures.

Normal growth versus genuine problem

The central diagnostic question is whether observed memory usage is normal or a problem. The distinction:

SignalNormalProblem
Absolute memory usageHigh but stableHigh and climbing
Correlation with queue depthMemory tracks queue depthMemory grows independent of queue
Swap activityNone or minimalActive swap-in and swap-out
Trend over timeRises and falls with workloadMonotonic growth
OOM killer eventsNonePresent in kernel log

High absolute memory usage is not, by itself, a problem. A PowerMTA on a 32 GB server using 24 GB of RAM may be entirely healthy if that usage is stable, proportionate to queue depth and connection count, and not causing swapping. Memory exists to be used; an MTA holding queue metadata in RAM is doing exactly what it should.

The signals of a genuine problem are: memory that climbs without bound, memory growth uncorrelated with workload (queue depth stable but memory still rising), active swapping, and OOM killer events. Any of these indicates a real problem requiring action; their absence means the memory usage, however high in absolute terms, is normal.

Reading memory metrics

Standard Linux tools provide the memory metrics needed for diagnosis.

free shows overall memory state:

free -h

The output shows total, used, free, and critically the swap line. Used memory near total is fine if swap is not being used; used swap with activity is the danger signal.

vmstat shows memory activity over time:

vmstat 5

The si and so columns (swap-in and swap-out) are the key. Persistent non-zero values in si/so mean the system is actively swapping, which is the clearest memory-pressure danger signal.

The PowerMTA process specifically, via /proc or ps:

# PowerMTA process memory
ps -o pid,rss,vsz,cmd -p $(pgrep pmtad)

# Or watch it over time
watch -n 30 'ps -o rss,vsz -p $(pgrep pmtad)'

RSS (resident set size) is the physical memory the process actually occupies. Watching RSS over time reveals the trend: stable RSS (or RSS that rises and falls with queue depth) is healthy, monotonically climbing RSS uncorrelated with workload suggests a leak.

Kernel log for OOM events:

dmesg | grep -i "out of memory"
journalctl -k | grep -i oom

Any OOM killer events in the kernel log indicate the system has run out of memory and the kernel has killed processes; this is a serious memory problem.

The queue depth relationship

Because message queue metadata is the largest variable component of PowerMTA memory, queue depth and memory usage are tightly coupled. Understanding this relationship is central to memory diagnosis.

When queue depth rises, memory usage rises, because PowerMTA holds metadata for more messages. When queue depth falls (messages deliver or bounce), memory usage falls. A PowerMTA processing a campaign send sees memory rise as messages queue and fall as they deliver; this oscillation is entirely normal.

The diagnostic implication: before concluding PowerMTA has a memory problem, check queue depth. If memory is high and queue depth is also high, the memory usage is the honest cost of the queue, and the question becomes whether the queue depth itself is appropriate. If memory is high but queue depth is low, the memory usage is not explained by queue metadata and something else is consuming memory (accounting buffer accumulation, a leak, unusual connection count).

# Check queue depth alongside memory
pmta show queues | tail -20
free -h

Correlating these two tells you whether the memory usage is queue-driven (and therefore explained) or unexplained (and therefore worth investigating further).

Common causes of memory pressure

The common causes of genuine PowerMTA memory pressure:

Excessive queue depth. The most common cause. A deep queue holds metadata for many messages, consuming proportional memory. The queue may be deep for a legitimate reason (genuine high volume) or an inflated reason (stale messages accumulating because retry-max-time is too long, or messages stuck in backoff because of a reputation problem). When queue depth is the cause, the fix is whatever reduces queue depth: shorten retry-max-time so stale messages bounce sooner, add smtp-pattern-list bounce patterns to clear known-permanent failures, address the reputation problem causing backoff accumulation.

Accounting buffer accumulation. If PowerMTA generates accounting records faster than they flush to disk, the buffers grow. This happens when the accounting write path is slow (disk I/O bottleneck) or blocked (disk full, log rotation problem). The fix is restoring the accounting write path: free disk space, fix log rotation, ensure the downstream ingestion is consuming archived logs.

DNS cache growth. The DNS cache grows with distinct destination domains. This is rarely the dominant memory component, but a PowerMTA sending to an extremely diverse set of domains accumulates a larger cache. This is usually not worth specific tuning unless it is demonstrably the cause.

Connection state from high concurrency. A PowerMTA configured for very high concurrent connection counts holds proportional connection state. If max-smtp-conn is set very high and the deployment genuinely runs many thousands of concurrent connections, the connection state memory is real. This is proportionate to the configured and actual concurrency.

Genuine leak (rare). A genuine memory leak, where PowerMTA accumulates memory it never releases, is rare but possible. The signature is monotonic memory growth uncorrelated with queue depth, connection count, or any other workload metric. When a leak is suspected, a PowerMTA restart reclaims the memory while the cause is investigated, and the vendor should be consulted if the leak recurs.

Most PowerMTA memory pressure is queue depth

Before investigating exotic causes, check queue depth. The large majority of PowerMTA memory-pressure incidents trace to excessive queue depth, and the queue depth is itself usually inflated by a fixable cause: retry-max-time too long, no smtp-pattern-list bounce patterns, or a reputation problem causing backoff accumulation. Fix the queue depth and the memory pressure resolves. Reaching for a leak diagnosis or more RAM before checking queue depth skips the most likely explanation.

Swap activity as a danger signal

Swapping is the clearest and most important memory-pressure danger signal.

When physical RAM is exhausted, Linux moves memory pages to swap, which is disk-backed. Disk is orders of magnitude slower than RAM. A PowerMTA whose working memory is partly in swap performs terribly: every access to swapped memory incurs a disk read, delivery slows dramatically, and the whole system becomes sluggish and unpredictable.

The danger of swapping for PowerMTA specifically: PowerMTA is a latency-sensitive, high-throughput process. Swapping does not just slow it down proportionally, it can cause cascading problems. Slow processing means queue depth grows (messages arrive faster than they process), which means more memory needed, which means more swapping, a feedback loop that degrades the system progressively.

Detecting swap activity:

# Current swap usage
free -h

# Swap activity over time - watch si and so columns
vmstat 5

# Per-process swap usage
for pid in $(pgrep pmtad); do
    grep VmSwap /proc/$pid/status
done

Any sustained swap-in/swap-out activity for a PowerMTA host is a problem requiring action. The action is either reducing memory demand (cut queue depth) or increasing memory supply (add RAM), depending on whether the memory demand is legitimate.

A note on swappiness: the Linux vm.swappiness parameter controls how readily the kernel swaps. For a PowerMTA host, a lower swappiness (10-20 rather than the default 60) makes the kernel prefer to keep memory in RAM, which is generally desirable for a latency-sensitive process. But low swappiness does not solve genuine memory exhaustion; it just delays swapping. The real fix for memory exhaustion is matching memory supply to demand.

The OOM killer risk

The Linux Out Of Memory killer is the worst outcome of memory pressure. When the kernel cannot satisfy a memory allocation and cannot reclaim enough memory, it kills processes to recover memory.

If the OOM killer targets PowerMTA, the service goes down abruptly. PowerMTA is killed, queued messages stop processing, and the service is unavailable until it restarts (PowerMTA's pmtawatch watchdog may restart it, but the interruption still occurs). An OOM kill of PowerMTA is a service outage.

The OOM killer chooses targets based on a score that considers memory usage; a large process like PowerMTA on a memory-pressured host is a likely target. Detecting OOM events:

dmesg | grep -i "killed process"
journalctl -k --since "1 hour ago" | grep -i oom

If the OOM killer has killed PowerMTA, the kernel log shows it. This is a definitive sign that memory provisioning is inadequate for the workload, or that a memory problem (runaway queue, leak) has consumed all available memory.

Preventing OOM kills: the structural fix is ensuring memory supply exceeds the realistic peak demand, with headroom. A PowerMTA host should have enough RAM that even at peak queue depth and peak concurrency, memory usage stays comfortably below physical RAM with room for the OS and other processes. If the OOM killer is firing, that headroom does not exist, and the fix is either more RAM or less memory demand.

More RAM versus configuration fix

When PowerMTA has genuine memory pressure, the response is either adding RAM or fixing configuration. The choice depends on whether the memory demand is legitimate.

Add RAM when memory usage is proportionate. If the deployment genuinely processes enough volume and holds enough queue depth that the memory usage is the honest cost of the workload, the deployment has outgrown its RAM allocation. More memory is the correct answer. This is consistent with the general PowerMTA sizing guidance that memory matters more than CPU at scale: high-volume PowerMTA legitimately needs substantial RAM.

Fix configuration when memory usage is inflated. If the memory usage is inflated by a fixable cause, fixing the cause resolves the memory pressure without adding RAM:

  • Queue depth excessive because retry-max-time is too long: shorten retry-max-time so stale messages bounce sooner.
  • Queue depth excessive because of accumulating known-permanent failures: add smtp-pattern-list bounce patterns.
  • Queue depth excessive because of a reputation problem causing backoff accumulation: address the reputation problem.
  • Accounting buffers accumulating because the write path is slow or blocked: fix the accounting pipeline (disk space, log rotation, ingestion).

The diagnostic sequence: first determine whether queue depth is appropriate or inflated. If inflated, fix the cause and memory pressure resolves. If queue depth is appropriate and memory is still pressured, the deployment needs more RAM.

Adding RAM to mask a configuration problem treats the symptom while the cause continues. A PowerMTA with runaway queue depth from a too-long retry-max-time will fill whatever RAM it is given; adding RAM buys time but the queue continues growing. Fixing the configuration is the durable solution when a configuration cause exists.

Systematic diagnostic procedure

The procedure when PowerMTA memory usage is concerning:

Step 1: confirm whether it is actually a problem. Check for the genuine-problem signals: is the system swapping (vmstat si/so)? Is memory climbing without bound? Are there OOM events (dmesg)? If none of these, the high memory usage is likely normal and no action is needed.

Step 2: check queue depth. Run pmta show queues. Is the queue deep? Deep queue plus high memory means the memory is queue-driven and the question becomes whether the queue depth is appropriate.

Step 3: assess whether queue depth is appropriate or inflated. Is the queue deep because of genuine high volume, or because of stale messages, accumulating permanent failures, or backoff accumulation? Query the accounting log for the queue composition.

Step 4: if queue depth is inflated, fix the cause. Shorten retry-max-time, add smtp-pattern-list bounce patterns, or address the reputation problem. Memory pressure should resolve as queue depth falls.

Step 5: check accounting buffers. If memory is high but queue depth is low, check whether the accounting write path is healthy. Is the disk full? Is log rotation working? Is the downstream ingestion keeping pace?

Step 6: check for a leak. If memory grows monotonically and is uncorrelated with queue depth, connection count, and accounting buffers, suspect a leak. A restart reclaims the memory; if the growth recurs after restart, consult the vendor.

Step 7: if memory demand is legitimate, add RAM. If queue depth is appropriate, accounting is healthy, and there is no leak, but memory is still pressured, the deployment has outgrown its RAM. Provision more.

Step 8: prevent recurrence. Whatever the cause, set up monitoring: alert on swap activity, alert on memory usage approaching a threshold, alert on queue depth that would imply memory pressure. Catch the next occurrence before it reaches the OOM killer.

The memory alarm that was actually a retry-max-time problem

An operator we worked with got a memory alert on their PowerMTA host: memory usage had climbed to 90 percent of the 16 GB RAM and was still rising. Their first instinct was to add RAM. We ran the diagnostic. The system was not yet swapping but was close. pmta show queues revealed the cause immediately: queue depth was enormous, hundreds of thousands of messages, far more than the deployment's volume would explain. Investigating the queue composition showed it was dominated by old messages to a handful of destination domains, being retried over and over. The retry-max-time was set to 7 days, well beyond the default, and a deliverability event two weeks earlier had generated a flood of deferrals that were all still being retried. The fix was not more RAM. It was shortening retry-max-time to 2 days and adding smtp-pattern-list bounce patterns for the permanent failures hiding in the queue. Within hours PowerMTA bounced the stale messages, queue depth dropped to a normal level, and memory usage fell from 90 percent to around 40 percent without a single byte of RAM added. The lesson: a memory alarm is frequently a queue depth problem in disguise. Check queue depth before reaching for more RAM, because the cheapest fix for PowerMTA memory pressure is often a configuration change that the more-RAM instinct would skip right past.

PowerMTA memory diagnosis rests on one central distinction: high memory usage that is stable and proportionate to workload is normal and healthy, while memory that climbs without bound, causes swapping, or triggers the OOM killer is a genuine problem. The diagnostic path runs through queue depth, because message queue metadata is the dominant variable memory component and most memory-pressure incidents trace to excessive queue depth. The fix is frequently a configuration change (shorter retry-max-time, smtp-pattern-list bounce patterns) rather than more RAM, because inflated queue depth is a common and fixable cause. Operators who understand what PowerMTA holds in memory and why, who watch the genuine-problem signals rather than just the absolute number, and who check queue depth before assuming they need more hardware, diagnose memory incidents accurately and fix them at the root.

H
Henrik Larsen

Email Infrastructure Engineer at Cloud Server for Email. Diagnoses PowerMTA resource and performance incidents for ESP clients. Related: Messages Stuck in Queue, High-Volume Tuning, retry-after Directive and Backoff.