Batteries included UI to monitor your Messenger workers, transports, schedules, and messages.

Fund package maintenance!

Installs: 26 831

Dependents: 0

Suggesters: 0

Security: 0

Stars: 121

Watchers: 7

Forks: 12

Open Issues: 30


v0.1.0 2024-02-16 14:27 UTC

This package is auto-updated.

Last update: 2024-02-16 19:30:57 UTC


CI codecov

Batteries included UI to monitor your Messenger workers, transports, schedules, and messages.


If the packaged UI is not to your liking, you can easily build your own with the provided tools.


composer require zenstruck/messenger-monitor-bundle

messenger:monitor Command

With zero configuration, you can run the messenger:monitor command to see information about your running workers and transports. If storage is configured, it displays a historical snapshot whose period can be customized with the --period option.



This step is required to use the User Interface and History.


Only Doctrine ORM is currently available as a storage engine.


  1. Create a ProcessedMessage entity that extends Zenstruck\Messenger\Monitor\History\Model\ProcessedMessage in your app:

    // src/Entity/ProcessedMessage.php
    namespace App\Entity;
    use Zenstruck\Messenger\Monitor\History\Model\ProcessedMessage as BaseProcessedMessage;
    use Doctrine\ORM\Mapping as ORM;
    #[ORM\Entity(readOnly: true)]
    class ProcessedMessage extends BaseProcessedMessage
        private ?int $id = null;
        public function id(): ?int
            return $this->id;
  2. Add the entity class to the bundle config:

    # config/packages/zenstruck_messenger_monitor.yaml
                entity_class: App\Entity\ProcessedMessage
  3. Clear Cache:

    bin/console cache:clear
  4. Create and execute the migration:

    bin/console doctrine:migrations:diff
    bin/console doctrine:migrations:migrate


Once configured, consumed messages are tracked and saved. These processed messages contain a lot of useful information and can be viewed in the user interface or the provided tools.

Disable Monitoring

You may want to disable monitoring for certain messages. There are two ways to do this:

  1. When dispatching the message, add the DisableMonitoringStamp:
    use Zenstruck\Messenger\Monitor\Stamp\DisableMonitoringStamp;
    /** @var \Symfony\Component\Messenger\MessageBusInterface $bus */
    $bus->dispatch(new MyMessage(), [new DisableMonitoringStamp()])
  2. Add the DisableMonitoringStamp as a class attribute to your message:
    use Zenstruck\Messenger\Monitor\Stamp\DisableMonitoringStamp;
    class MyMessage
  3. You may want to disable monitoring for messages that are dispatched without any handler. You can do this by using the DisableMonitoringStamp with optional constructor argument true:
    use Zenstruck\Messenger\Monitor\Stamp\DisableMonitoringStamp;
    #[DisableMonitoringStamp(onlyWhenNoHandler: true)]
    class MyMessage


The stored ProcessedMessage has a description property. This is helpful to differentiate between messages in the user interface. By default, this is the stringified version of the message object (if it implements \Stringable). You can add the DescriptionStamp to customize:

use Zenstruck\Messenger\Monitor\Stamp\DescriptionStamp;

/** @var \Symfony\Component\Messenger\MessageBusInterface $bus */

$bus->dispatch(new MyMessage(), [new DescriptionStamp('some custom description')])


To help with filtering processed messages, they can have one or more tags. Some tags are added automatically (like schedule if it's a scheduled message) but you can also add your own in one of two ways:

  1. When dispatching the message, add one or more TagStamp's:
    use Zenstruck\Messenger\Monitor\Stamp\TagStamp;
    /** @var \Symfony\Component\Messenger\MessageBusInterface $bus */
    $bus->dispatch(new MyMessage(), [new TagStamp('tag-1'), new TagStamp('tag-2')])
  2. Add the TagStamp as a class attribute to your message:
    use Zenstruck\Messenger\Monitor\Stamp\TagStamp;
    class MyMessage

messenger:monitor:purge Command

If your app handles a lot of messages, the processed message database table will get very large. The messenger:monitor:purge clears messages older than a specific date: See Period for allowed values.

bin/console messenger:monitor:purge # by default, purges all messages older than 1 month

bin/console messenger:monitor:purge --older-than all
bin/console messenger:monitor:purge --older-than 1-day
bin/console messenger:monitor:purge --older-than 1-week

bin/console messenger:monitor:purge --exclude-schedules # ignore messages tagged with "schedule"


Schedule this command to run daily with symfony/scheduler and RunCommandMessage.

messenger:monitor:schedule:purge Command

If using symfony/scheduler, you might want to keep a specific # of these messages as they might run very infrequently. When running messenger:monitor:purge, add the --exclude-schedules option to avoid deleting schedule history. Then run messenger:monitor:schedule:purge to keep a specific number (10 by default) of task run histories.

bin/console messenger:monitor:schedule:purge # by default, keeps 10 runs for each task

bin/console messenger:monitor:schedule:purge --keep 5 # keep only 5

Use the --remove-orphans option to delete schedule task runs that are no longer associated with a schedule.

bin/console messenger:monitor:schedule:purge --remove-orphans


Schedule this command to run daily with symfony/schedule and RunCommandMessage.

User Interface


Storage must be configured for this feature.

Create a controller that extends Zenstruck\Messenger\Monitor\Controller\MessengerMonitorController in your app:

// src/Controller/MessengerMonitorController.php

namespace App\Controller;

use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Zenstruck\Messenger\Monitor\Controller\MessengerMonitorController as BaseMessengerMonitorController;

#[Route('/admin/messenger')] // path prefix for the controllers
#[IsGranted('ROLE_ADMIN')] // alternatively, use a firewall
final class MessengerMonitorController extends BaseMessengerMonitorController

You can now access the dashboard at: /admin/messenger or with the route zenstruck_messenger_monitor_dashboard.


It is important that your MessengerMonitorController is only accessible by site administrators as it contains sensitive application information. Use either the IsGranted attribute on your controller as shown above and/or ensure the controller is behind an access-controlled firewall that only allows site administrators.


Install knplabs/knp-time-bundle (composer require knplabs/knp-time-bundle) to display friendlier times and durations in the UI.


Install lorisleiva/cron-translator (composer require lorisleiva/cron-translator) to display friendlier CRON values for your scheduled tasks.

Advanced Usage

Workers Service


Transports Service



Schedules Service







Storage must be configured for this feature.

Storage Service



Full Default Bundle Configuration

        enabled:              false

        # Role required to view live components.
        role:                 ROLE_MESSENGER_MONITOR

            # Your Doctrine entity class that extends "Zenstruck\Messenger\Monitor\History\Model\ProcessedMessage"
            entity_class:         ~
      pool: app.cache # If using workers in docker. You can use shared cache pool for all workers
      expired_worker_ttl:  3600