85.1.41. pepsi-status

summarise the health of the Pepsi mail pipeline

Manual section:

1

85.1.41.1.1. Name

pepsi-status - print a human-readable health summary of the queue, stuck messages, delivery and failure statistics, and outbound TLS outcomes.

85.1.41.1.2. Synopsis

pepsi-status [GLOBAL-OPTIONS] [–json] [–limit N]

85.1.41.1.3. Description

pepsi-status prints a one-shot, read-only health report of the Pepsi pipeline, drawn entirely from the shared pepsi database. It is intended to be run interactively or over SSH (for example from a monitoring host); it never modifies a row and issues no database notification, so it is safe to run against a live deployment at any time.

The tool connects to the same database as the other components, through the shared [pepsi-postgres] section; it introduces no configuration of its own. When started as root it continues as the pepsi service account; see Running as root.

The report has six sections:

Queue

The current contents of the pepsi.workqueue table, broken down by stage and status, with the age of the oldest message in each bucket and a grand total. This is the live backlog: pending and running rows are work in progress, paused rows are waiting (for example for a payment deadline or a relay retry), and failed/timeout rows need attention.

Stuck messages

The individual messages whose status is failed or timeout, oldest first. For each, the report shows the workqueue_id, status, stage, age, envelope sender and subject, and — when a relay stage recorded one — the structured reason the next hop refused the message (remote MTA, SMTP code, enhanced status and reply text, or our own diagnostic, from state.bounce), the error text the stage recorded when it failed or deferred the message (state.last_error, shown as error:), and the reason pepsi-dispatch gave when it failed or timed out the message itself — a crashed or hung worker, or an unconfigured stage (state.dispatch_error, shown as dispatch:). In –json output the three are the bounce, last_error and dispatch_error fields of each problems entry. The list is capped by –limit; the true backlog size is always shown.

Delivery & failures

Cumulative pipeline counters: the total number of messages that completed and left the pipeline, the total stage executions, and per stage the number of executions it counted (the MESSAGES column, which is the denominator of the average), the average processing time, worker timeouts and worker crashes. These are lifetime totals accumulated since the schema was created. Because a successfully delivered message is deleted from the queue, there is no per-message delivery log and therefore no rolling time window: the figures are cumulative, not “in the last hour”.

Outbound TLS

Outbound TLS session outcomes over the last seven UTC report-days (today’s, in UTC, and the six before it, whatever the server’s time zone), grouped by policy domain and result type, with non-successful results marked. This data is only present when [pepsi-tlsrpt] SEND_REPORTS is enabled, and only for as long as pepsi-tlsrpt prune keeps it ([pepsi-tlsrpt] RETAIN_DAYS, also seven days by default), so a shorter retention shortens this section. The section then lists any MX-host addresses the resolver cache currently has marked as failing to connect, with the time of their last successful connection (or never).

Mailbox quotas

One line per local account Pepsi has accounted for, oldest measurement first: usage, effective limit, how full it is, where the figure came from (fsquota/maildirsize/scan) and how long ago the mailbox was measured. A row exists for every account Pepsi has delivered to — that is what creates one — whether or not it has a quota, so on a deployment that does not use the feature the section still lists those accounts, with - in the LIMIT and FULL columns. It is empty, and says so, only when no local delivery has been accounted for.

LIMIT is the limit actually in force, resolved exactly as the delivery path resolves it: the account’s own limit if it has one, otherwise the site default [pepsi] MAILBOX_QUOTA, with an explicit 0 on the account meaning unlimited rather than inherit, and the kernel’s own quota tightening whichever won.

USED is the last measurement plus everything delivered since; it only ever grows, because nothing tells Pepsi when a user deletes mail over IMAP, so it is an upper bound rather than a fact. MEASURED is how long ago somebody actually looked, and it matters because a refusal at RCPT rests on a measurement being fresh: an account showing “full” against a very old measurement is one pepsi-quota(1) reconcile should be looking at.

Correspondent key changes

How many correspondents’ encryption keys the Autocrypt newest-wins rule replaced in the last 30 days (rotated), and how many such replacements ACCEPT_ROTATION = expired refused (held), followed by the most recent of both — capped by –limit — with the date, the address, the protocol, the old and new fingerprints and whether the new key was the correspondent’s own (inbound) or introduced by a third party (gossip). A rotation is the one way a message that merely claims to come from a correspondent can change the key we encrypt to them with, which is why it is shown here rather than left in the audit log. The figures are read from the key.peer.rotate and key.peer.rotate.held rows of pepsi.event_log, which the database writes together with the change itself, so the section reaches back as far as [pepsi-admin] EVENT_RETENTION keeps them. See pepsi-stage-autocrypt-learn(1).

Free-form text in the human-readable report — a remote MTA’s reply, a message subject, a host name from the resolver cache — has its control characters replaced by spaces before it is printed. All of it is chosen by somebody else, and this tool is meant to be run over SSH against a live deployment, which is precisely where a terminal escape sequence would repaint the operator’s screen. The --json output is unaffected: JSON escaping already covers it.

85.1.41.1.4. Options

–json

Emit the whole report as a single JSON object (keys queue, problem_total, problems, global, stages, tls_report_days, tls, dns_failures, mailboxes, quota_default_bytes, key_change_days, key_changes_rotated, key_changes_held and key_changes) instead of the human-readable text, for scripting and monitoring.

–limit N

List at most N individual stuck (failed/timeout) messages and at most *N* mailbox-quota accounts. Defaults to 50. The reported total backlog is unaffected by this cap.

The stuck-message section says “showing X of Y” when it is cut off; the mailbox-quota section does not, so on a host with more than N accounted accounts that list is silently the N with the oldest measurements — which is the useful end of it, but raise –limit before concluding an account is absent.

85.1.41.1.5. Running as root

Pepsi gives every component its own PostgreSQL role, authenticated over the local socket by the operating-system account it runs as, and root is deliberately not one of them. Rather than fail to connect and oblige you to remember sudo -u pepsi pepsi-status, the tool detects that it was started as root and becomes the unprivileged pepsi service account before it connects; the report itself stays strictly read-only.

The configuration file (and any @inline-secret@ fragment it references) is read before the switch, so a root-only configuration is still loaded normally.

If the pepsi account does not exist — an uninstalled source tree, a test rig — the identity is left untouched, a warning is logged, and the connection is attempted as the invoking user, so a setup in which root can reach the database keeps working.

85.1.41.1.6. Global Options

These options may appear before or after the other options.

-c FILE, –config FILE

Read the configuration from FILE instead of searching the default locations.

-L LOGLEVEL, –log LOGLEVEL

Set the logging verbosity. LOGLEVEL is one of error, warn, info, debug or trace (default: info).

-v, –verbose

Show log messages from all sources, including third-party libraries.

-h, –help

Print a usage summary and exit.

-V, –version

Print the version and exit.

85.1.41.1.7. Exit Status

0

Successful completion.

1

An error occurred: a malformed configuration file or a failed database connection or query. The reason is written to the log.

85.1.41.1.8. Files

When –config is not given, the first existing file from the following list is used. Every Pepsi component shares the same configuration file, so this is the same list each one searches:

  • $XDG_CONFIG_HOME/pepsi.conf

  • $HOME/.config/pepsi.conf

  • /etc/pepsi/pepsi.conf

  • /etc/pepsi.conf

85.1.41.1.9. Examples

Print the full health report:

pepsi-status -c /etc/pepsi/pepsi.conf

Collect the report as JSON for a monitoring system over SSH:

ssh mail.example.com pepsi-status --json

Show up to 200 stuck messages:

pepsi-status -c /etc/pepsi/pepsi.conf --limit 200

85.1.41.1.10. See Also

pepsi-queue(1), pepsi-tlsrpt(1), pepsi-quota(1), pepsi-dispatch(1), pepsi.conf(5), pepsi-setup(1)

85.1.41.1.11. Bugs

Report bugs to the Pepsi issue tracker.