Features
SMTPBench takes key=value arguments, with no dashes and no flags. Four are required; everything else tunes scale, realism, transport, or output. Every option it accepts is listed here, with defaults as the tool actually behaves.
Accurate as of SMTPBench 1.2.1
Required
Four keys must be present. recipient can be substituted with recipient_file.
| Option | Default | Description |
|---|---|---|
| recipient | required | Target address. Or supply recipient_file= instead. |
| port | required | SMTP port: 587, 465, or 25. Optional with eml_out_dir= (offline), where nothing connects. |
| threads | required | Concurrent worker threads. |
| messages | required | Messages per thread, not total. 0 means run forever until interrupted. |
smtpbench [email protected] port=587 threads=5 messages=10
Load generation
How much traffic, and how fast.
| Option | Default | Description |
|---|---|---|
| rate | none | Whole-run cap in messages per second, enforced by a token bucket shared across all threads. Mutually exclusive with delay and random_delay. |
| delay | 0 | Fixed pause in seconds between messages, per thread. |
| random_delay | false | Random 1–15 second pause between messages, per thread. |
smtpbench [email protected] port=587 threads=10 messages=0 rate=10
smtpbench [email protected] port=587 threads=5 messages=100 delay=1
Targeting and delivery
Where the mail goes, and what happens when a host is down.
| Option | Default | Description |
|---|---|---|
| recipient_file | none | File of recipient addresses, one per line. Requires lb_host= or eml_out_dir=. |
| recipient_file_order | random | random or roundrobin. |
| lb_host | auto MX lookup | Send to this host directly, skipping DNS. |
| client_hostname | system hostname | Hostname announced in HELO/EHLO. |
| transaction_timeout | 20 | SMTP transaction timeout, in seconds. |
| max_retries | 3 | Retry attempts per message. |
| retry_delay | 20 | Seconds between retries. |
smtpbench [email protected] port=25 threads=10 messages=100
smtpbench [email protected] lb_host=smtp.dev.lets.qa port=587 threads=5 messages=10
smtpbench recipient_file=recipients.txt recipient_file_order=roundrobin \
from_file=senders.txt \
lb_host=smtp.dev.lets.qa port=587 threads=5 messages=100Sender identity and journaling
Journal mode exists for testing archiving and compliance paths. The same message also goes to a journaling address, the way a real archiving deployment would capture it.
| Option | Default | Description |
|---|---|---|
| from_address | no-reply@localhost | Sender address. |
| from_file | none | File of From addresses. |
| from_file_order | random | random or roundrobin. |
| journal | false | Enable journal mode: send a copy to a journaling address. |
| journal_address | same as recipient | Where journal copies go. |
| journal_file | none | File of journal addresses. Requires journal=true. |
| journal_file_order | random | random or roundrobin. |
smtpbench [email protected] port=587 threads=5 messages=10 \ journal=true [email protected]
Transport and authentication
| Option | Default | Description |
|---|---|---|
| tls_mode | starttls, or ssl on port 465 | starttls, ssl, or none. |
| use_tls | unset | Deprecated alias. true resolves to starttls, false to none. Prints a warning. |
| username | none | SMTP AUTH username. Discouraged on the command line. |
| password | none | SMTP AUTH password. Discouraged on the command line. |
| dotenv_path | auto-discovered | Path to a .env file for credentials. |
smtpbench [email protected] port=465 threads=5 messages=10 tls_mode=ssl
export SMTPBENCH_USER=myuser export SMTPBENCH_PASS=mypassword smtpbench [email protected] port=587 threads=5 messages=10
smtpbench [email protected] port=587 threads=5 messages=10 \ dotenv_path=/etc/smtpbench.env
Message realism
Real relays behave differently under 2MB attachments than under empty test messages.
| Option | Default | Description |
|---|---|---|
| attachment_path | none | Attach one specific file to every message. |
| attachment_size | none | Generate synthetic attachments. Fixed (512KB) or a range (10KB-2MB). |
| attachment_dir | none | Sample attachments from a corpus directory of real files. |
| attachment_probability | 1.0 | Chance from 0.0 to 1.0 that a given message gets attachments. |
| attachment_count | 1 | Attachments per message. Accepts a range (1-3). |
| attachment_filename | source or generated name | Override the filename. With count above 1 becomes payload-1.bin, payload-2.bin, and so on. |
| attachment_mime_type | auto-detected, else application/octet-stream | Override the MIME type. |
| body_text_dir | none | Prefix each body with a randomly chosen text file from this directory. |
smtpbench [email protected] port=587 threads=5 messages=20 \ attachment_dir=./corpus attachment_probability=0.8 attachment_count=1-3
smtpbench [email protected] port=587 threads=5 messages=50 \ attachment_size=10KB-2MB
smtpbench [email protected] port=587 threads=5 messages=20 \ body_text_dir=./text-corpus
Output and observability
| Option | Default | Description |
|---|---|---|
| logfile_output | ./logs | Directory for log files. |
| debug | false | Verbose debug logging, including SMTP protocol detail. |
Every message is traceable
Each message carries headers for correlation across distributed mail systems. The run UUID is constant per run; thread and message IDs vary per message. Find one message in a downstream mailbox and tie it back to the log line that produced it, or filter an entire run out of a shared mailbox afterwards.
X-SMTPBench-Run-UUID: a1b2c3d4-e5f6-7890-abcd-ef1234567890 X-SMTPBench-Thread-ID: 3 X-SMTPBench-Message-ID: 7
The summary artifact
Every run writes summary_{timestamp}_{uuid}.json. Field shapes below; the file also records elapsed_seconds and config.address_lists.
{
"run_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"config": {
"threads": 5, "messages": 100, "rate": null,
"tls_mode": "starttls", "auth": false, "port": 587, "offline": false
},
"totals": { "sent": ..., "failed": ..., "retried": ..., "success_rate": ... },
"latency_ms": { "p50": ..., "p95": ..., "p99": ..., "max": ... },
"per_mx": { "mx1.dev.lets.qa": { "sent": ..., "failed": ... } }
}Offline mode
Fully offline: no DNS or MX lookup, no banner check, no connection. The recipient is used only as a message header, and since 1.2.1 no port is needed.
| Option | Default | Description |
|---|---|---|
| eml_out_dir | none | Write each composed message to this directory as {sha256}.eml instead of sending. |
smtpbench [email protected] threads=5 messages=20 \ eml_out_dir=./eml-output
smtpbench [email protected] threads=2 messages=10 \ eml_out_dir=./eml-output attachment_dir=./corpus \ attachment_probability=0.5 body_text_dir=./text-corpus
Getting help
--help, -h, help, ?, --version, -v, version, and zero arguments all short-circuit before any parsing, so they are always safe to run.
smtpbench --help # full option list and examples smtpbench --version