Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Monitor Bundle Laravel Package

whatwedo/monitor-bundle

whatwedo/monitor-bundle is a Symfony bundle for application health monitoring. Define checks, run them via console or HTTP endpoints, and review status in a dashboard to catch service failures and regressions early.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framework mismatch. This is a Symfony bundle, not a Laravel package. There is no service provider, no config/monitor.php, and it will not install into a Laravel app. How you use it depends on which side you're on:

  • You're maintaining a Symfony app → use it directly as below.
  • You're in Laravel and evaluating this as a reference → read it as a spec for what a health endpoint should look like, then implement with spatie/laravel-health (the mature Laravel equivalent) or ~80 lines of your own route + check classes. Don't try to bridge the container.

Symfony install (3 steps):

composer require whatwedo/monitor-bundle

Register the bundle in config/bundles.php (Flex usually does this for you), then hit the monitoring route — confirm the exact path in the README, it's typically /monitor:

curl -i http://localhost:8000/monitor

You should get JSON describing overall status plus per-check results, with HTTP 200 for healthy and a non-2xx for failures.

Where to look first, in order:

  1. README.md — route path, config tree, and the check interface name.
  2. src/Controller/ — what the endpoint actually returns; this is your integration contract.
  3. src/Check/ (or equivalent) — the built-in checks (database, cache, etc.) and the interface you'll implement for your own.
  4. DependencyInjection/Configuration.php or config/*.yaml — the real config keys, not the ones in blog posts.

Your first use case: don't build a dashboard. Point an existing prober at it — Uptime Kuma, Healthchecks.io, Pingdom, or a Docker HEALTHCHECK. The bundle's job is to answer "is this app able to serve traffic?" in one HTTP call; the prober's job is alerting and history.

Implementation Patterns

Prober-first integration. The endpoint exists to be polled every 15–60s by something outside the app. Wire it into:

# docker-compose.yml
healthcheck:
  test: ["CMD", "curl", "-fsS", "http://localhost:8000/monitor"]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 20s

Split liveness from readiness. A single endpoint that includes a database check is a liveness endpoint that will restart your pods during a DB failover. If the bundle only exposes one route, keep the built-in route as readiness (dependencies included) and add a trivial static controller for liveness:

#[Route('/healthz', methods: ['GET'])]
public function live(): Response
{
    return new Response('ok', 200);
}

Custom check for your domain. The pattern is always the same: implement the bundle's check interface, return a status plus a human message, and let autoconfiguration pick it up (verify the DI tag in the bundle's service config — if autoconfigure isn't on, you'll need to tag it explicitly in services.yaml).

final class QueueWorkerCheck implements CheckInterface
{
    public function check(): CheckResult
    {
        $last = Cache::get('queue:heartbeat');
        $stale = $last === null || $last < time() - 300;

        return new CheckResult($stale ? 'error' : 'ok', 'queue worker heartbeat');
    }
}

Pair it with a scheduled job or a worker loop that writes queue:heartbeat every minute. This catches the failure mode that plain DB checks miss: the app is fine, the workers are dead, and jobs are silently piling up.

Make each check cheap and bounded. Health checks run forever, on a timer, on every instance. A check that does SELECT COUNT(*) on a 50M-row table, or calls a third-party API with a 30s timeout, will eventually be the reason your monitoring is unreliable. Cache expensive check results for ~30s and set explicit connect timeouts on DB/Redis clients.

Use it as a build/deploy gate. Run the same endpoint in CI after deploy and fail the pipeline on non-200 — cheaper than a smoke-test suite and it exercises the real container wiring (env vars, DB credentials, cache connectivity).

In Laravel, the equivalent shape:

Route::get('/monitor', function () {
    $checks = [
        'database' => rescue(fn () => DB::select('select 1'), fn () => false, false) !== false,
        'cache'    => rescue(fn () => Cache::put('hc', 1, 10), fn () => false, false),
        'queue'    => Cache::get('queue:heartbeat', 0) > now()->subMinutes(5)->timestamp,
    ];

    return response()->json(['checks' => $checks], in_array(false, $checks, true) ? 503 : 200);
})->middleware('throttle:60,1');

Gotchas and Tips

The HTTP status is the API. Most probers and all container orchestrators ignore the JSON body and key off the status code. If a check fails but the endpoint still returns 200, your monitoring is decorative. Verify with curl -i after deliberately breaking a dependency (stop the DB, hit the endpoint, confirm 5xx).

It is a Symfony bundle — there is no Laravel path. Bundle registration, the DI container, and the config tree are all Symfony concepts. Forcing a bridge (custom service provider that boots a Symfony kernel) is far more work than reimplementing three checks. spatie/laravel-health exists specifically for this.

Don't let /monitor be public. The JSON response typically names your services and versions — a free recon endpoint. Restrict by network (internal: true in Docker, no ingress rule), by middleware IP allowlist, or by bearer token in the prober config.

A hanging check is worse than a failing one. No timeout on the DB connection means your 5s probe timeout fires and you get a false alarm that resolves on retry — alert noise that trains people to ignore alerts. Set connect_timeout on PDO/Redis to something under your probe's timeout.

Cache checks need a round-trip, not a ping. Redis::ping() succeeding tells you the socket is open. Writing a key with a short TTL and reading it back tells you the cache is actually usable (correct DB index, not read-only replica, not out of memory).

Config only loads in the right environment. If the route 404s, check in order: is the bundle in config/bundles.php? Is the route file imported for that env? bin/console debug:router | grep -i monitor and bin/console debug:container --tag=whatwedo_monitor.check (confirm the tag name in the bundle source) will tell you in seconds what guessing won't.

Alpine containers may not have curl. curl -f is the right call, but a slim/alpine image often lacks it. Either install it in the Dockerfile or use wget --spider -q, which behaves differently on non-2xx — test the healthcheck in isolation before trusting it.

Adoption risk. Three stars, zero dependents, one release in the recent past. That's not a reason to avoid it — it's MIT and small enough to read end to end — but it is a reason to (a) pin the exact version in composer.json, (b) keep your custom checks in your own namespace so they survive a swap to a different bundle, and (c) be prepared to fork. The maintenance cost of a health endpoint is low; the lock-in cost should be too.

Extension points to prefer over forking: custom CheckInterface implementations for domain logic, your own controller/route when you need a different response shape, and an event listener if you need to attach context (build SHA, region) to responses. Only touch the bundle's own classes if you're fixing a bug — and then open the PR, because you're now the maintainer.

Debugging loop that actually works: curl -i locally → if status is wrong, check the controller; if status is right but a specific check is wrong, bin/console debug:container to confirm your check is registered; if it isn't registered, it's autoconfiguration or a missing tag, not your logic.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings