Search by

jmonitor / easyadmin-bundle

johndodev

Live monitoring pages inside EasyAdmin: system, PHP, FrankenPHP, Apache, Nginx, MySQL, PostgreSQL, Redis and Symfony metrics collected when you open the page, no worker required.

Package info

github.com/jmonitor/easyadmin-bundle

Homepage

Type:symfony-bundle

pkg:composer/jmonitor/easyadmin-bundle

Statistics

Installs: 279

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.6.0 2026-10-22 15:29 UTC

This package is auto-updated.

Last update: 2026-10-23 17:13:57 UTC


README

This bundle adds live monitoring pages to your EasyAdmin backend: system, PHP, FrankenPHP, Apache, Nginx, MySQL, PostgreSQL, Redis and Symfony metrics, collected when you open the page and refreshed while it stays open.
No worker to run, nothing to store.

Packagist Version Tests License Last Commit

jmonitor/jmonitor
 
jmonitor/collector
 
jmonitor/jmonitor-bundle
 
Self-hostable backend — not needed with the cloud version The collectors — install them in the project to monitor Symfony-specific integration of the collectors
jmonitor/easyadmin-bundle you are here
Live monitoring pages inside EasyAdmin — no backend, no worker
https://jmonitor.io

Symfony tab of the monitoring page in EasyAdmin PHP tab of the monitoring page in EasyAdmin

Supported components

Category Components
Runtime PHP FrankenPHP
Web server Apache Nginx
Framework Symfony
Databases & cache MySQL PostgreSQL Redis
System Linux

Requirements

  • PHP Version
  • Symfony Version
  • EasyAdmin Version

Getting started

1. Install the bundle

composer require jmonitor/easyadmin-bundle

If you do not use Symfony Flex, add the bundle to config/bundles.php and install its assets:

return [
    // ...
    Jmonitor\EasyAdminBundle\JmonitorEasyAdminBundle::class => ['all' => true],
];
php bin/console assets:install

2. Add a menu link

In your dashboard controller, link to the <dashboard route>_jmonitor_index route (admin is the route name of your dashboard, adapt it):

public function configureMenuItems(): iterable
{
    // ...
    yield MenuItem::linkToRoute('Monitoring', 'internal:desktop', 'admin_jmonitor_index');
}

The pages live under the path of your dashboard (/admin/jmonitor for a dashboard at /admin), so the access_control rule protecting your backend covers them.

Configuration

Everything is optional. Components are detected from your application.

# config/packages/jmonitor_easyadmin.yaml
jmonitor_easyadmin:
    cache_pool: cache.app      # PSR-6 pool keeping the collected metrics
    polling_interval: 10       # seconds between two live refreshes, 0 disables them
    static_ttl: 3600           # seconds during which versions, configuration and OS data are kept
    permission: ~              # role or voter attribute required to see the pages, e.g. ROLE_SUPER_ADMIN
    exclude_connections: []    # Doctrine connections the MySQL and PostgreSQL tabs leave out, e.g. '*_backup'
    components:
        system: auto           # auto | true | false
        php: auto
        frankenphp:
            enabled: auto
            endpoint: http://localhost:2019/metrics   # Prometheus endpoint of the Caddy admin API
        apache:
            enabled: auto
            endpoint: http://localhost/server-status  # mod_status page, read as ?auto
        nginx:
            enabled: auto
            endpoint: http://localhost/nginx_status   # stub_status page
        symfony:
            enabled: auto
            messenger: true    # runs "messenger:stats", defaults to true when Messenger is installed on Symfony 7.2+
            scheduler: true    # runs "debug:scheduler", defaults to true when Scheduler is installed
            flex: true         # runs "composer recipes", defaults to true when Flex is installed
        mysql:
            enabled: auto
            connections: []    # Doctrine connection names, empty = every MySQL/MariaDB connection
        postgresql:
            enabled: auto
            connections: []    # Doctrine connection names, empty = every PostgreSQL connection
            schema: public     # schema whose tables feed the storage, scans and dead rows cards
        redis:
            enabled: auto
            connections: []    # id => DSN, {dsn} or {service}; empty = the Symfony cache provider or REDIS_URL

auto shows the component when it is detected. true always shows it, with the detection error when it fails. false removes it.

permission restricts the pages to the users granted that role or voter attribute, on every dashboard, on top of the security of the dashboard itself; the others get a 403. Hide the menu link the same way: MenuItem::linkToRoute(...)->setPermission('ROLE_SUPER_ADMIN').

connections lists the Doctrine connections to show, each one becoming a pill under the tabs (MySQL groups them by server: a pill per server, then the databases of the selected server); a connection named here is always shown, with its error when it cannot be read. A PostgreSQL connection may have its own schema: connections: { default: ~, dwh: { schema: analytics } }. The former connection option still works and is deprecated.

exclude_connections leaves out of the MySQL and PostgreSQL tabs the Doctrine connections whose name matches one of its patterns, with the * and ? wildcards: the connections your application declares but does not serve, for instance exclude_connections: ['*_backup']. A connection listed in connections cannot match it.

For Redis, connections maps an id to a DSN, { dsn: ... } or { service: ... } (a \Redis, \Predis\Client or \Relay\Relay service), each one becoming a pill: connections: { cache: 'redis://localhost', sessions: { dsn: 'redis://sessions:6379/1' }, queue: { service: app.redis_queue } }.

Compared to jmonitor.io

This bundle shows the present only: nothing is collected while nobody looks at the page, and nothing is kept.

jmonitor.io, the cloud edition, collects your metrics continuously with a worker and keeps their history: the dashboards draw their charts, and alerts notify you by email when a threshold is crossed or a version reaches its end of life.

Both use the same collectors and run side by side: install jmonitor/jmonitor-bundle, fill its API key and run its worker to send the same metrics to jmonitor.io.

Good to know

  • System: Linux only.
  • FrankenPHP: needs the metrics global option in the Caddyfile ({ metrics }), and frankenphp in the PATH of the web process for the version.
  • Apache: needs a mod_status page at /server-status (SetHandler server-status, Require local), left out of the rewrite to index.php. When Apache runs in another container than PHP, point components.apache.endpoint at it.
  • Nginx: needs location = /nginx_status { stub_status; allow 127.0.0.1; deny all; } in the server block answering localhost. When Nginx runs in another container than PHP, point components.nginx.endpoint at it.
  • MySQL: the slow queries need read access to the performance_schema.
  • PostgreSQL: the slow queries need the pg_stat_statements extension and GRANT pg_monitor TO <your role>.
  • Symfony: the Messenger stats need Symfony 7.2+, the Flex recipes composer in the PATH of the web process.
  • Load balancer: the System, PHP, web server and Symfony tabs show the server that answered; in containers, give each one its own hostname.
  • Outbound requests: the version badges come from endoflife.date and the update badges of the Overview from GitHub, once a day; the requests carry no data about your application.

Security

  • The pages show data meant for administrators, such as query texts, which may hold personal data.
  • They answer under every dashboard of the application, linked from its menu or not: with a dashboard for less trusted users, set permission or add an access_control rule on ^/<dashboard path>/jmonitor.
  • A dashboard host does not apply to these pages: restrict the host in the firewall or in access_control.
  • Keep the Caddy admin API, the mod_status page and the stub_status page reachable from the web host only.
  • With a Content-Security-Policy, allow https://dash.jmonitor.io in frame-src for the help panel.

Need help?