wijourdil/ntfy-notification-channel
Send Laravel notifications through ntfy, the simple HTTP-based push service. This channel delivers messages to your phone or desktop via public or self-hosted ntfy servers using Laravel's familiar notification system.
Architecture fit. The package is a thin adapter between Laravel's Notification system and the ntfy HTTP publish API. Architecturally it is a single channel class implementing send($notifiable, Notification $notification) that POSTs a message body plus ntfy headers (Title, Priority, Tags, Click, Actions) to {base_url}/{topic}. It sits alongside mail, database, slack, and broadcast in the via() array and inherits Laravel's queueing, retry, and event semantics for free. ntfy itself is a Go single-binary pub/sub server with a dead-simple REST surface, so there is no protocol complexity or SDK to track — the integration surface is one HTTP call. Fit is best for internal/ops alerting in a Laravel monolith or a small number of services, not as a customer-facing notification path.
Integration feasibility. High. Typical adoption is composer require wijourdil/ntfy-notification-channel, publish the config, add NtfyChannel::class to via(), and implement toNtfy() on the notification. On-demand routing (Notification::route('ntfy', $topic)) is the key question — if unsupported, ad-hoc alerting to a per-tenant or per-environment topic becomes awkward and forces dedicated notifiable models. Self-hosted ntfy (Docker, with token/basic auth and ACLs) should be the default target; the public ntfy.sh instance raises data-residency and rate-limit concerns that a TPM should resolve before coding starts.
Technical risk. The dominant risk is dependency hygiene, not technology. 23 stars, 0 dependents, and a single maintainer is a weak ecosystem signal: no community regression coverage, no evidence of a release cadence, and a meaningful probability of abandonment. Mitigating factors are strong — the code footprint is small enough to audit in an hour, MIT licensed, and the underlying protocol is stable and trivially reimplementable (~40 lines against Illuminate\Http\Client). Secondary risks: unverified Laravel 11/12 (illuminate/*) and PHP 8.x version constraints; whether failures surface as exceptions (needed for queue retries) or are swallowed into a boolean; missing feature parity for attachments, delayed delivery, action buttons, and priority mapping; and no test suite or CI visible.
Key questions.
illuminate/notifications, illuminate/support, PHP) and is Laravel 12 declared?send() throw on non-2xx responses and network timeouts, or return silently? Retry-on-failure behavior depends entirely on this.Http facade (mockable via Http::fake()) or a hard-wired Guzzle client? Testability hinges on it.Notification::route) and queued (ShouldQueue) delivery supported?Stack fit. Laravel 9–12 / PHP 8.1+ applications using the standard Notification pipeline. Composes cleanly with existing channels — the lowest-risk pattern is adding ntfy as an additional channel on notifications that already go to mail or Slack, so a channel failure degrades a duplicate rather than the primary path. For simple cases, Laravel 11's native http notification channel is a viable zero-dependency substitute; this package's value is only the ergonomics of a toNtfy() contract.
Migration path. Start in a branch with a self-hosted ntfy instance or a private test topic. Pin the exact version ("wijourdil/ntfy-notification-channel": "1.0.0", not ^1.0) given the low release velocity — caret ranges on a dormant package provide no upgrade value and only risk a compromised or broken tag. Wrap the channel behind an internal interface or, at minimum, centralize topic/base-URL resolution in config and a small factory so the package is replaceable without touching notification classes. If the package is later abandoned, swap to a first-party channel class in a single PR.
Compatibility. Validate composer.json constraints against the app's Laravel and PHP versions before committing; a missing ^12.0 constraint will block the upgrade path later and is the most likely cause of forced removal. Confirm the config file shape and that auth headers are configured (not hardcoded) so staging and production topics diverge cleanly. Confirm Laravel's HTTP client timeouts and Http::retry() are applied, either in the package or by wrapping the call.
Sequencing.
curl publish path and auth.Http::fake() tests asserting the outgoing request shape so the contract is locked regardless of upstream drift.http channel; migrate if the package is unmaintained.Maintenance. Near-zero routine maintenance for a small, stable adapter, but ownership must be explicit: there is no vendor, so the team owns the code the moment it ships. Treat the package as vendorable — fork it into the org if policy requires, or keep a pinned version with a documented re-audit cadence. Watch for silent breakage on Laravel major upgrades; add a smoke test in CI that asserts the channel still resolves and emits the expected request.
Support. Single-maintainer, best-effort, no SLA. Internal runbooks must cover: how to rotate ntfy access tokens, how to add a topic for a new service, and how to disable the channel via a feature flag if ntfy is unreachable. Document that the channel is advisory — it must never be the only delivery path for a paging-critical alert.
Scaling. Laravel's queue absorbs fan-out; ntfy self-hosted handles high message rates with modest resources (single binary, SQLite or external cache). Constraints to plan for: the public ntfy.sh instance enforces rate limits and message-size caps (~4KB payload) and is unsuitable for production volumes or sensitive content; per-topic subscriber counts and attachment sizes drive self-hosted sizing. Horizontal queue workers need no coordination beyond the shared config.
Failure modes. Non-2xx from ntfy (401/403 on token or ACL misconfiguration, 404 on topic namespace errors, 413 on oversized payloads, 429 on rate limits); DNS/TLS/timeouts on the ntfy host; ntfy process or disk down. The critical unknown is whether the channel surfaces these as exceptions — if it swallows them, queue retries never fire and alerts are lost silently. Mandate explicit tries/backoff on queued notifications, a failed() handler that routes to a fallback channel (mail or Slack), and alerting on NotificationFailed events. Avoid attaching sensitive payloads to public topics; prefer short, non-PII messages with a link.
Ramp-up. Low. A mid-level Laravel developer is productive in under an hour given the single-class surface and the straightforward ntfy header API. The real ramp-up cost is operational, not developer: someone must own the self-hosted ntfy deployment, TLS, auth, and topic ACL model. Budget a half-day for that setup and an hour for the package spike; anything longer signals the package is fighting the framework version and should be replaced with a native http channel implementation.
How can I help you explore Laravel packages today?