sameoldnick / laravel-ntfy
A Laravel package for integrating ntfy with your application.
Requires
- php: ^8.4
- illuminate/contracts: ^11.0||^12.0||^13.0
- verifiedjoseph/ntfy-php-library: ^4.8
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/fortify: ^1.37
- laravel/pint: ^1.30
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^11.0.0||^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- spatie/laravel-ray: ^1.35
This package is auto-updated.
Last update: 2026-08-26 18:33:46 UTC
README
A Laravel package for sending ntfy push notifications. It provides a native Laravel notification channel and a standalone service, built on the Ntfy PHP Library.
- Requirements
- Installation
- Configuration
- Sending notifications
- Sending messages directly with the service
- Testing
- Handling responses
Requirements
- PHP 8.4+
- Laravel 11 / 12 / 13
Installation
Install the package via Composer:
composer require sameoldnick/laravel-ntfy
The package's service provider and Ntfy facade are registered automatically via package discovery — no manual registration is required.
Publish the configuration file:
php artisan vendor:publish --tag=ntfy-config
Optional: per-user configuration model
If you want to store each user's ntfy destination (server, topic, credentials) in the database, publish and run the migration that creates the ntfy_configurations table:
php artisan vendor:publish --tag=ntfy-migrations php artisan migrate
You can skip this step if you only send messages to hard-coded or config-driven destinations using the standalone service (see below).
Configuration
The published config/ntfy.php file exposes the following environment variables:
| Environment variable | Default | Description |
|---|---|---|
NTFY_ENABLED |
false |
Enables/disables the notification channel. The standalone service is always available regardless of this setting. |
NTFY_SERVER_URL |
https://ntfy.sh/ |
The ntfy server URL. |
NTFY_DEFAULT_TOPIC |
null |
A default topic used when none is specified. |
NTFY_AUTH_USERNAME |
"" |
Username for basic auth (user method). |
NTFY_AUTH_PASSWORD |
"" |
Password for basic auth (user method). |
NTFY_AUTH_TOKEN |
"" |
Access token for token auth (token method). |
NTFY_HTTP_TIMEOUT |
10 |
Request timeout in seconds. |
NTFY_HTTP_CONNECT_TIMEOUT |
10 |
Connection timeout in seconds. |
NTFY_HTTP_VERIFY_SSL |
true |
Whether to verify SSL certificates. |
NTFY_HTTP_RETRY_ENABLED |
true |
Whether failed requests are retried. |
NTFY_HTTP_RETRY_MAX_ATTEMPTS |
3 |
Number of retry attempts. |
NTFY_HTTP_RETRY_DELAY |
100 |
Delay between retries in milliseconds. |
Note:
ntfy.enabledonly controls the notification channel. When it isfalse, notifications routed throughvia('ntfy')are skipped (with a log entry), but you can still send messages directly through theNtfyservice/facade.
Sending notifications
This is the standard Laravel approach: route a Notification to a notifiable (e.g. a User). There are two parts — telling Laravel where to send the message, and telling it what to send.
1. Set the destination
There are two ways to define where ntfy messages for a notifiable should be delivered.
Option A — the NtfyConfiguration model (per-user, stored in the database).
Add the NtfyNotifiable trait to your notifiable model. This adds a ntfyConfiguration() morph-one relationship and resolves the destination from the ntfy_configurations table automatically.
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use SameOldNick\Ntfy\Concerns\NtfyNotifiable; class User extends Authenticatable { use NtfyNotifiable; }
Then store the user's destination, for example:
$user->ntfyConfiguration()->create([ 'server_url' => 'https://ntfy.example.com', 'topic' => 'alerts', 'auth_token' => 'tk_...', // or 'username' + 'password' ]);
Option B — manual routing (hard-coded destination).
Define a routeNotificationForNtfy() method on the notifiable, returning an array (or a ServerInfo instance — see below):
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { public function routeNotificationForNtfy($notification = null) { return [ 'server_url' => 'https://ntfy.example.com', 'topic' => 'alerts', // Use one of these for authentication (optional): // 'auth_username' => 'user', // 'auth_password' => 'password', // 'auth_token' => 'tk_...', // Per-user HTTP options (overrides the 'ntfy.http' config): // 'options' => [], ]; } }
2. Build the message
Create a notification that sends via the ntfy channel and defines a toNtfy() method:
<?php namespace App\Notifications; use Illuminate\Notifications\Notification; use Ntfy\Message; use SameOldNick\Ntfy\Services\MessageBuilder; class MyNotification extends Notification { public function via(object $notifiable): array { return ['ntfy']; } public function toNtfy(object $notifiable): Message { return MessageBuilder::make() ->title('This is the title') ->body('This is the message') ->priority(3) ->tags(['green', 'red']) ->build(); } }
Note: The topic set on the destination (
routeNotificationForNtfy/NtfyConfiguration) always takes precedence over any topic set on the message.
3. Send it
use App\Notifications\MyNotification; $user->notify(new MyNotification);
Sending messages directly with the service
When you don't have a notifiable (for example, a CLI command, scheduled job, or health check), send a message directly through the Ntfy facade:
use Ntfy\Exception\EndpointException; use Ntfy\Exception\NtfyException; use SameOldNick\Ntfy\DTOs\ServerInfo; use SameOldNick\Ntfy\Facades\Ntfy; use SameOldNick\Ntfy\Services\MessageBuilder; $message = MessageBuilder::make() ->title('Server down') ->body('The web server is unreachable.') ->priority(5) ->build(); $server = ServerInfo::createWithToken( url: 'https://ntfy.example.com', topic: 'alerts', token: 'tk_...', ); try { $response = Ntfy::send($message, $server); // Message sent successfully. $response->id(); // unique message ID } catch (NtfyException|EndpointException $ex) { // Handle the error. }
ServerInfo factory methods
ServerInfo describes the destination. Use one of these helpers:
// Read the destination from the 'ntfy.global' config block. $server = ServerInfo::fromConfig(); // Build from an array (e.g. the output of routeNotificationForNtfy). $server = ServerInfo::fromArray([ 'server_url' => 'https://ntfy.example.com', 'topic' => 'alerts', 'auth_token' => 'tk_...', ]); // Username/password authentication. $server = ServerInfo::createWithAuth( url: 'https://ntfy.example.com', username: 'user', password: 'password', topic: 'alerts', ); // Token authentication. $server = ServerInfo::createWithToken( url: 'https://ntfy.example.com', token: 'tk_...', topic: 'alerts', ); // No authentication. $server = ServerInfo::createWithoutAuth( url: 'https://ntfy.example.com', topic: 'alerts', );
MessageBuilder methods
MessageBuilder is a fluent builder that wraps the underlying Ntfy\Message. Commonly used methods:
$message = MessageBuilder::make() ->topic('alerts') // usually overridden by the destination topic ->title('Title') ->body('Plain-text body') ->markdown('# Markdown body') ->priority(3) // 1 (low) - 5 (high) ->tags(['warning', '🚨']) ->click('https://example.com') // URL opened when the notification is clicked ->icon('https://example.com/icon.png') ->schedule('10m') // delay delivery, e.g. "30s", "1h", "1d" ->attach('https://example.com/file.pdf', 'report.pdf') ->email('ops@example.com') ->build();
Any other method on Ntfy\Message can be called directly thanks to method forwarding.
Testing
Use the Ntfy facade's fake() helper to prevent real HTTP requests and assert on messages in your tests:
use SameOldNick\Ntfy\Facades\Ntfy; Ntfy::fake(); // ... run code that sends notifications ... Ntfy::assertSent(fn ($message) => $message->getData()['title'] === 'Server down'); Ntfy::assertSentCount(1); Ntfy::assertNothingSent();
Handling responses
A successful send returns a MessageResponse object with these accessors:
$response = Ntfy::send($message, $server); $response->id(); // unique message ID $response->time(); // unix timestamp $response->dateTime(); // DateTimeImmutable / Carbon instance $response->topic(); // topic the message was sent to $response->message(); // body content $response->title(); // title $response->priority(); // priority (1-5)
License
This package is open-sourced software licensed under the MIT license.