Search by

honchoagency / craft-deadman

mijewe

Package info

github.com/honchoagency/craft-deadman

Documentation

Type:craft-plugin

pkg:composer/honchoagency/craft-deadman

Statistics

Installs: 31

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.3.0 2026-09-25 15:27 UTC

This package is auto-updated.

Last update: 2026-09-25 15:28:21 UTC


README

Banner

Deadman

A general-purpose dead-man's-switch monitoring plugin for Craft CMS 5.

Requires Craft CMS ^5.9 and PHP ^8.2.

TL;DR

Deadman runs configured health checks on a schedule and pings an external monitor (StatusCake Push, Healthchecks.io, etc.) when a check passes. If a check doesn't pass, the absence of an alert is what the external service reacts to.

The problem

There are many aspects of a site that can break unnoticed. Maybe your queue is stuck due to a crashed daemon; maybe your contact form hasn't had a submission for a week; maybe a regular FeedMe import has stopped running. By setting up Deadman alerts, you can be alerted as soon as these things fail.

Deadman evaluates whatever checks you define and pushes to an external monitor only when a check passes. If a check starts failing, or Deadman itself stops running, the push simply stops arriving and your monitor's own silence-based alerting takes it from there.

Quick start

  1. Install the plugin
  2. Copy the config file and define your checks in config/deadman.php.
  3. Schedule php craft deadman/run via cron
  4. 🎉

From there, each due check runs on schedule, pings its configured URL on pass, and stays quiet on failure.

Installation

1. Install Deadman

You can install Deadman by searching for "Deadman" in the Craft Plugin Store, or install manually using composer.

composer require honchoagency/craft-deadman

2. Configure the plugin

Copy the default config file into your project:

cp vendor/honchoagency/craft-deadman/src/config.php config/deadman.php

Then edit config/deadman.php to define your checks.

Included checks

FailedJobCountCheck

Fails once too many jobs in Craft's own queue have failed. Takes a threshold (int) param: the number of failed jobs tolerated before the check fails.

OldestPendingJobCheck

Fails if the oldest job still waiting in the queue has been sitting there longer than expected. This is the check that catches a dead or stuck queue daemon. Takes a thresholdMinutes (int) param: how long a pending job is allowed to wait before it's considered stuck.

RecentFormieSubmissionCheck

Fails if a Formie form hasn't received a real (non-spam) submission within a window. This is useful for catching a form that's silently broken. Takes a thresholdHours (int, defaults to 24) param and an optional formHandle to scope the check to one form; omit it to check submissions across all forms.

Bring Your Own Checks

You can build your own custom checks by implementing CheckInterface and returning a CheckResult from the check's run() method.

For example, if you wanted to check if a regular import has been run:

<?php

namespace modules\mysite\deadman;

use honchoagency\deadman\checks\CheckInterface;
use honchoagency\deadman\models\CheckResult;

class ImportFreshnessCheck implements CheckInterface
{
    public function run(array $params): CheckResult
    {
        $isFresh = /* your own logic here, using $params */ true;

        return $isFresh
            ? CheckResult::pass('Import is up to date.')
            : CheckResult::fail('Import looks stale.');
    }
}

Then point a config/deadman.php entry's type at it, same as a prefab check:

'importFreshness' => [
    'type' => \modules\mysite\deadman\ImportFreshnessCheck::class,
    'params' => ['maxAgeHours' => 26],
    'successUrl' => getenv('DEADMAN_IMPORT_URL'),
],

3. Set up the cron job

# Run all due, enabled checks (eg every 5 minutes)
*/5 * * * * php craft deadman/run

Configuration

Everything is defined per check entry in config/deadman.php:

Key Type Notes
displayName ?string A human-readable label shown in the CP (settings, the utility) in place of the handle. Falls back to the handle if omitted.
enabled bool Defaults to true. Disabled checks are skipped.
displayCpAlert bool Defaults to false. Surfaces a CP alert banner while this check is currently failing — see CP alerts below.
cpAlertMessage ?Closure(CheckResult): string Overrides the CP alert's default "{label} is failing." text. Only used when displayCpAlert is true.
cpAlertUrl ?string Overrides the CP alert's action button destination — a CP-relative path or a full URL. Defaults to the Deadman utility.
cpAlertLabel ?string Overrides the CP alert's action button text. Defaults to "View details".
type class-string A class implementing CheckInterface. Mutually exclusive with checkFn.
params array Passed to a class-based check's run().
checkFn Closure(): CheckResult An inline check. Mutually exclusive with type.
interval int Minimum seconds between runs. Defaults to 300.
successUrl ?string Pinged (GET) when the check passes.
onFalse ?Closure(CheckResult): void Called when the check returns a failing result.
onException ?Closure(\Throwable): void Called when the check itself throws.

Exactly one of type or checkFn must be set per entry, or an InvalidConfigException is thrown.

Usage

# Run all due, enabled checks
php craft deadman/run

# Run one check by handle
php craft deadman/run --handle=failedJobs

# Run every enabled check regardless of interval / last-run state
php craft deadman/run --force

Disabling Deadman on an environment

Set DEADMAN_ENABLED=false (also accepts 0/no/off) in an environment's .env to stop craft deadman/run from evaluating or pushing anything there — useful for staging/QA copies of a production database+config, so they don't start alerting on behalf of an environment nobody's monitoring. Defaults to enabled; omit the var entirely on environments that should run normally.

CP alerts

Set displayCpAlert => true on a check to also surface a banner alert across the control panel while that check is currently failing (based on its last recorded state — the same state shown on the Deadman utility page, under Utilities → Deadman). It's off by default; turn it on for whatever's worth someone noticing without needing to check the external monitor.

Each flagged, currently-failing check gets its own banner — if three are failing at once, that's three separate alerts, not one combined summary. Each has an action button, "View details" linking to the Deadman utility by default — but that isn't always the most useful destination. Set cpAlertUrl/cpAlertLabel to point it somewhere more relevant instead:

'cpAlertUrl' => 'utilities/queue-manager', // a CP-relative path, or a full URL
'cpAlertLabel' => 'Go to Queue Manager',

The banner is only shown to users with the View Deadman alerts in the control panel permission (deadman:viewAlerts), under Settings → Users → [group] → Deadman. Admins always see it regardless, so leaving the permission ungranted is the simplest way to keep alerts admin-only.

By default an alert just says "{label} is failing." — set cpAlertMessage on a check to write your own instead:

'cpAlertMessage' => function (CheckResult $result): string {
    // $result is reconstructed from the check's last recorded outcome, not a
    // fresh run — treat it as historical, potentially stale-by-seconds data.
    return "There have been no Formie submissions recently ({$result->message})";
},

Respects DEADMAN_ENABLED above — an environment with Deadman disabled won't show CP alerts either, since its check state may just be a stale copy of another environment's.

Testing your setup

craft deadman/test/seed-jobs pushes a batch of jobs onto Craft's queue that always fail when run — useful for confirming FailedJobCountCheck and OldestPendingJobCheck actually catch problems before you rely on them.

# Push 5 jobs (default)
php craft deadman/test/seed-jobs

# Push 20, each delayed 30s before becoming eligible for pickup
php craft deadman/test/seed-jobs --count=20 --delay=30

Leave them unprocessed (queue daemon stopped, or pushed with --delay) to exercise OldestPendingJobCheck; run php craft queue/run to let them fail and exercise FailedJobCountCheck.

State and history

The plugin keeps a deadman_checkstates table with a single, overwritten row per check handle (last run time and most recent outcome). It exists only to answer "is this check due yet?" — it is not a history log. For historical debugging, log from the onFalse / onException closures and read Craft's log files.

Credits

Built by honcho.agency