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

Ntfy Php Library Laravel Package

verifiedjoseph/ntfy-php-library

A lightweight PHP library for sending push notifications through ntfy. It wraps ntfy's HTTP publish API, letting you send messages to topics from PHP apps and scripts with minimal setup—useful for monitoring alerts, job notifications, and server events.

Deep Wiki
Context7

Getting Started

1. Install and confirm compatibility

composer require verifiedjoseph/ntfy-php-library

Before writing any code, open composer.json in vendor/verifiedjoseph/ntfy-php-library and check two things: the PHP version constraint (it must overlap with the PHP version your Laravel app is pinned to) and the HTTP client dependency. This package is framework-agnostic, so it will pull in an HTTP client (Guzzle/PSR-18) rather than using Laravel's Http facade — that distinction drives most of the integration and testing decisions below.

2. Read the source, not just the README

The library is small. Spend ten minutes in src/ and identify:

  • the client class you instantiate (base URL + topic),
  • the message object or options array that carries title, priority, tags, click, actions, attach, delay,
  • the authentication entry point (basic auth, access token, or none),
  • the exception types thrown on non-2xx responses.

That is the entire surface area. There is no service provider, facade, or config file to learn.

3. Wire it into config/services.php

// config/services.php
'ntfy' => [
    'url'   => env('NTFY_URL', 'https://ntfy.sh'),
    'topic' => env('NTFY_TOPIC'),
    'token' => env('NTFY_TOKEN'),
    'timeout' => (int) env('NTFY_TIMEOUT', 3),
],

4. First use case: an ops alert

The highest-value first integration is "tell me when something breaks or ships" — not end-user notifications.

// app/Services/NtfyNotifier.php
namespace App\Services;

use VerifiedJoseph\Ntfy\Ntfy; // verify exact FQCN in vendor/src

class NtfyNotifier
{
    public function send(string $message, string $title = '', array $options = []): void
    {
        try {
            $client = new Ntfy(config('services.ntfy.url'), config('services.ntfy.topic'));
            // $client->send($title, $message, $options); // per README signature
        } catch (\Throwable $e) {
            report($e); // never let alerting break the request
        }
    }
}

Prove it works from php artisan tinker on a scratch topic before touching production code. Subscribe with ntfy subscribe my-test-topic in a terminal so you can see the exact payload shape arriving.

Implementation Patterns

Bind a singleton, not a client per call. Construct the client once in a service provider and inject NtfyNotifier where needed. Instantiating per-send is cheap, but a singleton gives you one place to attach a timeout, a logger, or a mock.

// app/Providers/AppServiceProvider.php
$this->app->singleton(NtfyNotifier::class, fn () => new NtfyNotifier(
    config('services.ntfy.url'),
    config('services.ntfy.topic'),
    config('services.ntfy.token'),
));

Per-environment and per-service topics. Treat the topic as the routing key. A typical scheme:

myapp-production-errors      (high priority, tags: rotating_light)
myapp-production-deploys     (low priority, tags: rocket)
myapp-staging-all            (default priority)

Derive the topic from app()->environment() and a purpose constant so staging alerts never wake anyone up.

A custom notification channel is the cleanest Laravel seam. Rather than sprinkling NtfyNotifier calls through controllers, implement Illuminate\Notifications\Notification with a toNtfy() method and a channel class whose send() delegates to the library. That gives you via() routing, ShouldQueue, and per-notifiable topics for free:

public function via($notifiable): array { return ['ntfy']; }

public function toNtfy($notifiable): array
{
    return [
        'topic'    => $notifiable->ntfy_topic,
        'title'    => 'Backup finished',
        'message'  => "Snapshot {$this->id} uploaded",
        'priority' => 'low',
        'tags'     => ['floppy_disk'],
    ];
}

Where it earns its keep day to day

  • Exception handler hooks (report() / a Handler::report override) for 500s — but rate-limit or dedupe, or a looping error will spam you.
  • failed() on queued jobs, with the job class, exception message, and a click URL to Horizon/your log viewer.
  • Scheduled task output: wrap Schedule::call() results and send a one-liner.
  • CI/deploy scripts: php artisan ntfy:deploy "$GIT_SHA" from your pipeline — this is often the trigger to adopt the library at all.
  • Long-running artisan commands that finish after you've walked away.

Use the rich message fields; they're the reason to pick this over raw curl. priority (1–5 or min/low/default/high/urgent), tags for emoji, click for a deep link, actions for a "view" button to a dashboard or an "http" button that hits a webhook, and attach for files (log excerpts, screenshots). A "view" action pointing at the failing job is the single most-used feature in practice.

Multi-tenant: build clients from user data, not config. Store each user's topic (and optional token) in the database and construct a fresh client per send. If you allow ntfy.sh for users, restrict what they can set — the topic is a capability URL.

Self-hosted deployments. Point NTFY_URL at your instance; ntfy requires base-url and behind-proxy settings server-side for correct click-through links. If you front it with an internal CA, you'll need the certificate trusted by the PHP process's CA bundle.

Testability. If the library accepts an injected PSR-18 client, pass a MockHandler in tests and assert on the outgoing request body/headers. If it doesn't, wrap it in your own NtfyNotifier interface and bind a fake in the container — that's the safer long-term coupling regardless. Never let a test suite hit a real topic.

Gotchas and Tips

No facade, no auto-discovery. Coming from Laravel packages, it's easy to assume Ntfy::send() exists after composer require. It doesn't. Everything goes through a manually constructed object, and there's no config file to publish.

Never let alerting take down the request. Every call must be wrapped. A DNS blip, a 429 from ntfy.sh, or a self-hosted instance being down should degrade to report() / a log line, never a 500 in your checkout flow. Set an aggressive connect timeout (1–3s) — the default client timeout can silently add seconds to a web response.

Public ntfy.sh topics are guessable and open. Anyone who knows or brute-forces a topic name can read your alerts and publish fake ones. Use long random strings (Str::random(32)) or self-host with auth. This is the most common "we'll fix it later" mistake with ntfy.

Topic names are secrets in logs. When debugging, mask the topic and access token. Laravel's exception reporting will happily dump your config array.

Message body is capped (~4KB) on ntfy.sh. Truncate exception traces and stack dumps; put the full detail behind a click URL. Attachments have their own size limit and are disabled on some servers.

Header vs. JSON publishing matters for unicode. If the library encodes fields as HTTP headers, non-ASCII titles/tags can break or get dropped. Prefer the JSON publish path if the library exposes it, and sanity-check with an emoji in the title.

Queued notifications can double-delay. ntfy's delay/scheduled delivery plus a queue worker means an alert can arrive twice as late as intended. Use one mechanism, not both.

Duplicate alerts are the classic failure mode. Exception handlers that fire per-request plus a retrying job will produce a burst of identical pushes. Add a short cache-based dedupe (Cache::add("ntfy:{$hash}", true, 60)) or rely on ntfy's cache headers deliberately.

Testing fakes. Http::fake() does not intercept Guzzle used directly — use the library's injectable client or your own wrapper interface. If you see real requests in tests, you've mocked at the wrong layer.

Debugging checklist, in order: (1) reproduce with curl against the same URL/topic/token; (2) ntfy subscribe <topic> in a terminal to see what actually arrives; (3) log the effective base URL — a trailing slash or missing scheme is a frequent constructor mistake; (4) check TLS errors for self-hosted instances; (5) verify token/permissions per topic, since ntfy auth is topic-scoped and a 403 looks identical to a wrong topic.

Extension points are thin by design. There's no event system or middleware pipeline. If you need request logging, retries, or metrics, wrap the library in your own service (or subclass and override the HTTP call) rather than expecting hooks. That wrapper is also your insurance policy if the package stalls — the API surface you use is small enough to reimplement in an afternoon if needed.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log