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

Symfony Health Check Bundle Laravel Package

macpaw/symfony-health-check-bundle

A Symfony bundle that adds configurable health check endpoints to your app, with built-in and custom checks for databases, cache, and other services. Expose readiness and liveness status for monitoring, load balancers, and Kubernetes.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the bundle with Composer and register it:

composer require macpaw/symfony-health-check-bundle
// config/bundles.php
return [
    // ...
    MacPaw\SymfonyHealthCheckBundle\MacPawSymfonyHealthCheckBundle::class => ['all' => true],
];

The first place to look is the bundle’s src/Check directory in the repository — that is the canonical list of bundled checks (Doctrine/DBAL, Redis, RabbitMQ, disk, memory, etc.) and the exact class names and configuration parameters they accept. Mirror those class names in your config rather than guessing from blog posts.

Add a minimal config and hit the endpoint:

# config/packages/macpaw_health_check.yaml
macpaw_health_check:
    checks:
        - MacPaw\SymfonyHealthCheckBundle\Check\DoctrineCheck
        - MacPaw\SymfonyHealthCheckBundle\Check\RedisCheck
curl -i http://localhost:8000/health
# 200 OK + JSON payload when every check passes
# 503 Service Unavailable + JSON payload describing the failing check(s) otherwise

The first real use case is load-balancer or Kubernetes probe wiring: point readinessProbe at /health (deep checks against DB/Redis/queue) and keep livenessProbe shallow — or a separate endpoint — so a transient dependency outage does not cause the orchestrator to restart otherwise healthy pods. Confirm the route name and default path in the bundle’s routing/config before wiring probes, since that is the contract your infrastructure will depend on.

If you use the Redis check, note that as of v2.1.0 the package allows predis/predis ^2.2, so a project already on Predis 2.x no longer needs a Composer conflict dance to install it. Everything in this assessment applies to the 2.x line; v2.1.0 is a maintenance release with no breaking changes.

Implementation Patterns

Start shallow, then go deep. Enable only the checks that map to something your SLA actually cares about. A /health endpoint that pings four external systems on every 2-second probe will itself become a source of load and flakiness. Typical progression: start with disk + memory (self-contained), then add DB, then queue/broker checks once you have timeouts configured on the clients.

Custom checks. Implement the bundle’s check interface (found in the repo alongside the built-in checks) and return a result object rather than throwing. Autoconfiguration via the interface or the bundle’s tag means you usually only need to register the class as a service:

final class S3BucketCheck implements /* bundle check interface */
{
    public function __construct(private S3Client $s3) {}

    public function check(): HealthCheckResult
    {
        try {
            $this->s3->headBucket(['Bucket' => 'uploads-prod']);
            return HealthCheckResult::ok();
        } catch (\Throwable $e) {
            return HealthCheckResult::fail('S3 unreachable: ' . $e->getMessage());
        }
    }
}

Return a fail result for expected dependency problems so the endpoint emits 503 (a known-bad state) instead of 500 (an unhandled exception), which is what most probes and dashboards expect to differentiate.

Split endpoints by audience. If the bundle supports multiple check groups (or you register a second controller), expose /health/live (process-only, always fast) separately from /health/ready (dependencies). Kubernetes, ECS, and most LBs let you point liveness and readiness at different paths — use that instead of forcing one endpoint to serve both purposes.

Cache expensive checks. Health endpoints are hit constantly. If the package exposes a result-cache TTL, set something on the order of 5–15 seconds for probes touching DB/Redis and leave the self-contained checks uncached. This bounds the number of real dependency round-trips regardless of how aggressive your LB is.

Pick one Redis client deliberately. RedisCheck supports both the redis extension and Predis; v2.1.0 widened the Predis constraint to ^2.2. The client you install determines how errors surface (exceptions vs. false returns), so assert your check actually fails — stop Redis locally and curl the endpoint — rather than assuming the adapter handles both paths identically.

Integration tips.

  • Expose the route only on internal listeners/networks; do not publish it through your public CDN or API gateway unless you deliberately want an unauthenticated dependency map.
  • Emit the JSON body into your observability pipeline (log shipper or a metrics exporter) so a failing check becomes a dashboard signal, not just a 503 in an access log.
  • Use the per-check context/message field to include the failing host or queue name — that is what turns a page into a one-minute fix.
  • If you already have an HealthCheckController-style endpoint in your app, retire it in favour of this bundle so there is one source of truth.

Gotchas and Tips

  • Liveness ≠ readiness. Wiring a DB check to a liveness probe is the classic outage amplifier: the DB blips, every pod is restarted, the DB is hit by a thundering herd of cold starts, and the blip becomes an incident. Keep liveness self-contained.
  • Timeouts belong on the client, not the check. Set connect/read timeouts (Redis, PDO, AMQP, HTTP) to a fraction of your probe timeout. A check that hangs blocks the whole response and the orchestrator sees a timeout rather than a clean 503.
  • Caching can hide a broken dependency. A short TTL is fine; a long one makes /health green during a real outage. If you cache, also alert on the underlying client errors directly.
  • Exception vs. result. Any uncaught exception inside a check will typically surface as 500. Wrap external calls in try/catch and translate to a fail result so the response body explains which dependency is down.
  • Doctrine and read replicas. A DB check may open a connection on every probe and can land on a replica or a primary depending on your connection config. Be explicit about which connection the check uses, especially in multi-entity-manager setups.
  • Response shape assumptions. Some load balancers want a plain-text OK body or a specific status code range. If yours does, wrap or override the controller rather than fighting the JSON format.
  • Debugging tagged services. Use bin/console debug:container --tag=<bundle-check-tag> (the tag name is in the bundle’s services.yaml) to confirm which checks are actually registered — a silently unregistered custom check is the most common “why is my check not running?” issue.
  • Dependency constraints move between minors. v2.1.0 was almost entirely a relaxations release (predis/predis ^2.2). If you were previously held back on an older Predis, run composer update macpaw/symfony-health-check-bundle predis/predis --with-dependencies and re-verify the Redis check's failure mode after the bump — a client major/minor change can alter exception types your try/catch relies on.
  • Config key drift. The bundle config root (macpaw_health_check) and the check class names are your integration surface. Pin the package version in composer.json (a caret on ^2.1 is fine given the 2.x history) and re-check the README on upgrade; there are no dependents listed, so you will not get community warning shots.
  • Maturity check before critical-path use. With ~62 stars and zero dependents, treat this as a small, single-maintainer package: read the src/ code (it is small enough to audit in an afternoon), fork it if you need guaranteed fixes, and keep your custom checks decoupled from its result classes so you can swap the transport later. v2.1.0 added a second maintainer to the commit log, which is mildly reassuring but does not change the risk profile.
  • Extension points. The practical ones are: custom checks (interface/tag), the config root for enabling/disabling bundled checks, and the controller/route if you need a different path or response format. Anything beyond that likely means overriding the bundle’s service definitions in services.yaml.
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.
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
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager