
Documentation
TYPO3 Extension “Mail Queue”
Version 1.7.0 (2026-09-12)
1. Install the Package
composer require b13/mail-queue
2. Activate the Spool
// config/system/settings.php or additional.php
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_spool_type']
= \B13\MailQueue\Mail\SendFirstSpool::class;
Without this line the send-first spool is inactive: mails are not intercepted or queued. The backend module and the console commands still exist, but they have nothing to process unless something else wrote to the table.
3. Create the Table
vendor/bin/typo3 extension:setup --extension=mail_queue
Any other schema update works too—the Install Tool's "Analyze Database Structure", or database:updateschema if you use helhum/typo3-console.
Re-run this after upgrading whenever ext_tables.sql gains columns, for example redirected_to for Send to address traceability.
4. Schedule the Two Jobs
Use the TYPO3 Scheduler task "Execute console commands" or a system cron. Without flush, failed mails stay queued forever.
# every minute—retries due mails and evaluates webhook health
* * * * * cd /path/to/project && vendor/bin/typo3 mailqueue:flush
# daily—retention (sent, failed, optionally queued) plus orphan payload files
15 3 * * * cd /path/to/project && vendor/bin/typo3 mailqueue:cleanup
Production Checklist
- Spool active—
transport_spool_typeset toSendFirstSpool - Schema present—table
tx_mailqueue_messageexists mailqueue:flushruns every minute (Scheduler or cron)mailqueue:cleanupruns dailyalertWebhookUrlpoints at an HTTPS endpoint. Ops email alone is not enough: when the queue is red, SMTP is usually broken too, so the alarm mail may never leave the box. See Webhook Alerting- On staging and test, use
forceHoldand setqueuedRetentionDays. Under force hold every mail stays queued forever; without a retention—or a periodicmailqueue:purge—the table and the payload directory grow without bound var/mailqueue/is readable by the flush user and survives deploys—see below
The var/mailqueue/ Directory
Attachment payloads are written there by the web-server user during a request and read by the—often different—CLI or cron user that flushes the queue. Two things follow:
Permissions. The extension applies your instance's fileCreateMask, folderCreateMask, and createGroup to the directory and its files, so a standard multi-user setup works out of the box. If you tighten those, keep the flush user able to read them, for example through a shared createGroup.
Deployment. Make var/mailqueue/ a shared or linked directory in your deployment, the way you already treat var/log, so payloads of mails queued before a release survive the deploy.
A payload that cannot be read is not treated as failed. The entry stays queued with a clear error message and is retried once the cause is fixed, and the attempt counter is left untouched because no delivery was tried.
Smoke Test
This takes the exact same path as any application mail, including the spool:
vendor/bin/typo3 mailqueue:send-test [email protected] # plain mail
vendor/bin/typo3 mailqueue:send-test [email protected] --attachment # file-backed queue path
Note what --attachment does and does not prove. The payload only goes to var/mailqueue/ when the delivery fails, or when archive mode is on. On a healthy SMTP server both runs simply send, and neither touches the directory.
To actually verify the file-backed path—the part where permission and deployment problems show up—point the instance at an unreachable SMTP server, run the --attachment command, and check that a file appears in var/mailqueue/ and that the entry flushes once SMTP is back.
Dashboard Widget
With typo3/cms-dashboard installed (a suggested dependency), a "Mail Queue Status" widget is available—a simple traffic light:
- Green—every mail went out directly, the queue is empty
- Orange—mails are queued, retries are running
- Red—at least one mail could not be delivered for longer than the alert threshold (30 minutes by default, configurable), or at least one entry has status
failed
The widget works on TYPO3 v13.4 and v14. Without the dashboard package the extension simply skips the widget registration.

Installation